 /*
 * Ingenic IMP FrameSource.
 *
 * Copyright (C) 2015 Ingenic Semiconductor Co.,Ltd
 * Author: Niky <xianghui.shen@ingenic.cn>
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <fcntl.h>
#include <semaphore.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <sys/prctl.h>
#include <linux/videodev2.h>
#include <linux/v4l2-mediabus.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <constraints.h>
#include <imp/imp_log.h>
#include <imp/imp_utils.h>
#include <pthread.h>
#include <imp/imp_osd.h>
#include <imp/imp_encoder.h>
#include <dsystem/funcs.h>
#include "framesource.h"
#include "fifo.h"
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
enum funcs_misc {
	DBG_MISC_SAVE_PIC,
	DBG_MISC_SNAP_PIC,
	DBG_MISC_SYSTEM_INFO,
	DBG_MISC_SIMPLE_CMD,
};

enum funcs_fs {
	DBG_FS_INFO,
	DBG_FS_IVDC_S,
};

#include <icommon.h>

#define TAG "Framesource"

#define SIZE_OF_RESIZE_TMP_BUF(_W, _H) (sizeof(uint16_t) * (_W * 5 + _H * 6 + 2000))
#define NR_MAX_FRAME_DELAY (100)

char *user = "imp_framesource";

static int sensor_width = 0;
static int sensor_height = 0;
static Framesource *gFramesource = NULL;
extern int64_t timestamp_base;
extern void filter2D(uint8_t *_dst, uint8_t *_src, int width, int height, int filterWindow, int thres);
extern void filter3D(uint8_t* _dst, uint8_t* _srcOld, uint8_t* _srcNew, int width, int height, int thres);
//extern void c_resize_simd(uint8_t* src, uint8_t* dst, int src_w, int src_h, int dst_w, int dst_h, bool is_clip, int clip_offset, int clip_w, int clip_h, c_csp_t format, uint16_t *resize_buf);
extern void c_resize_c(uint8_t* src, uint8_t* dst, int src_w, int src_h, int dst_w, int dst_h, bool is_clip, int clip_offset, int clip_w, int clip_h, c_csp_t format, uint16_t *resize_buf);

extern int nv12_scaler_16(uint8_t* src_y, uint8_t* dst_y, uint8_t* src_uv, uint8_t* dst_uv, uint16_t src_w, uint16_t src_h, uint16_t dst_w, uint16_t dst_h, uint8_t ratio_w, uint8_t ratio_h, uint8_t pad_en);
extern int nv12_scaler_8(uint8_t* src_y, uint8_t* dst_y, uint8_t* src_uv, uint8_t* dst_uv, uint16_t src_w, uint16_t src_h, uint16_t dst_w, uint16_t dst_h, uint8_t ratio_w, uint8_t ratio_h, uint8_t pad_en);
extern void nv12_scaler_8_init(uint16_t src_w, uint16_t src_h, uint16_t dst_w, uint16_t dst_h, uint32_t sstep, uint32_t* row_index, uint32_t* col_index);
extern int nv12_scaler_8_v3(uint8_t* src_y, uint8_t* dst_y, uint8_t* src_uv, uint8_t* dst_uv, uint16_t src_w, uint16_t src_h, uint16_t dst_w, uint16_t dst_h, uint32_t* row_index, uint32_t* col_index, uint8_t pad_en);
//extern int nv12_scaler_8_v3(uint8_t* src_y, uint8_t* dst_y, uint8_t* src_uv, uint8_t* dst_uv, uint16_t src_w, uint16_t src_h, uint16_t dst_w, uint16_t dst_h, uint8_t ratio_w, uint8_t ratio_h, uint8_t pad_en);
extern int nv12_scaler_8_v4(uint8_t* src_y, uint8_t* dst_y, uint8_t* src_uv, uint8_t* dst_uv, uint16_t src_w, uint16_t src_h, uint16_t dst_w, uint16_t dst_h, uint8_t ratio_w, uint8_t ratio_h, uint8_t pad_en);
void (*sw_resize)(uint8_t* src, uint8_t* dst, int src_w, int src_h, int dst_w, int dst_h, bool is_clip, int clip_offset, int clip_w, int clip_h, c_csp_t format, uint16_t *resize_buf);
static bool sw_resize_use_simd = 0;

static Framesource *get_framesource(void)
{
	return gFramesource;
}

int set_framesource_fps(uint32_t fps_num, uint32_t fps_den)
{
	if(!gFramesource){
		return -1;
	}
	gFramesource->inFrmRateNum = fps_num;
	gFramesource->inFrmRateDen = fps_den;
	return 0;
}

int set_sensor_resolution(int width, int height)
{
    if ((width % 16) || (height % 8)) {
        IMP_LOG_ERR(TAG, "Invalid sensor resolution(%dx%d)\n", width, height);
        return -1;
    }

    sensor_width = width;
    sensor_height = height;

    return 0;
}

int get_sensor_resolution(int *width, int *height)
{
    *width = sensor_width;
    *height = sensor_height;
    return 0;
}

int set_framesource_changewait_cnt(void) {
	if(!gFramesource) {
		return -1;
	}
	gFramesource->is_changwait_cnt = (gFramesource->inFrmRateNum/gFramesource->inFrmRateDen) * gFramesource->FS_EnablePHYChn_Num;
	return 0;
}
void list_add(struct FSDepth *new, struct FSDepth *prev, struct FSDepth *next)
{
	next->prev = new;
	new->next = next;
	new->prev = prev;
	prev->next = new;
}

void list_del(struct FSDepth **new, struct FSDepth *prev, struct FSDepth *next)
{
	*new = prev->next;
	next->prev = prev;
	prev->next = next;
	(*new)->next = *new;
	(*new)->prev = *new;
}

void list_add_head(struct FSDepth *new, struct FSDepth *head)
{
	list_add(new, head, head->next);
}

void list_del_head(struct FSDepth **new, struct FSDepth *head)
{
	list_del(new, head, head->next->next);
}

int is_list_empty(struct FSDepth *head)
{
	return head->next == head;
}

void list_del_FSDepth(struct FSDepth *entry)
{
	entry->next->prev = entry->prev;
	entry->prev->next = entry->next;
	entry->next = entry;
	entry->prev = entry;
}

static int get_frame(IMPFrameInfo *frame, void *pri)
{
	Framesource *framesource = (Framesource *)pri;
	FSChannel *channel = &framesource->channel[frame->pool_idx];
	struct v4l2_buffer buf;

	memset(&buf, 0, sizeof(struct v4l2_buffer));
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_USERPTR;
	//IMP_LOG_DBG(TAG, "@-@ start %s[%d] poolid = %d @@\n",__func__,__LINE__, frame->pool_idx);
	if (-1 == ioctl (channel->fs_fd, VIDIOC_DQBUF, &buf)) {
		IMP_LOG_ERR(TAG, "%s: VIDIOC_DQBUF failed, channel->fs_fd = %d, chnNum=%d\n", __func__, channel->fs_fd, frame->pool_idx);
		return -1;
	}

	if(buf.index >= (channel->attr.nrVBs+channel->fifo_frame_maxcnt)){
		IMP_LOG_ERR(TAG, "%s: buf.index >= total_buf_nums \n", __func__);
		return -1;
	}
	channel->dqframecnt++;

	frame->index = buf.index;
	frame->size = buf.length;
	frame->timeStamp = (int64_t)((int64_t)buf.timestamp.tv_sec * 1000000 + (int64_t)buf.timestamp.tv_usec);
	frame->timeStamp_ivdc = (int64_t)((int64_t)buf.timestamp.tv_sec * 1000000 + (int64_t)buf.timestamp.tv_usec);
	frame->timeStamp -= timestamp_base;

	//printf("@-@ %s[%d] poolid = %d index = %d size = %d @@\n",__func__,__LINE__, frame->pool_idx, buf.index, buf.length);
	//IMP_LOG_DBG(TAG, "end @-@ %s[%d] poolid = %d index = %d size = %d @@\n",__func__,__LINE__, frame->pool_idx, buf.index, buf.length);

	uint32_t frm_num = buf.sequence;
	//printf("### channel%d, frm_num = %d\n", channel->index, frm_num);
	((IMPFrameInfoPriv*)(frame->priv))->ncuinfo.frm_num = frm_num;
	((IMPFrameInfoPriv*)(frame->priv))->frame_type = buf.reserved&0xffff;
	((IMPFrameInfoPriv*)(frame->priv))->sensor_id = (buf.reserved >> 16)&0xffff;
#if CONFIG_TOOLCHAIN472
	((IMPFrameInfoPriv*)(frame->priv))->ivdc_overflow_cnt = buf.input;
#else
	((IMPFrameInfoPriv*)(frame->priv))->ivdc_overflow_cnt = buf.reserved2;
#endif
	((IMPFrameInfoPriv*)(frame->priv))->data_threshold = channel->data_threshold;

	//get true frame not tmp
	IMPFrameInfo *finfo = vbm_get_frame(frame->pool_idx, buf.index);
	if (finfo) {
		((IMPFrameInfoPriv*)(finfo->priv))->dqcount++;
	} else {
		IMP_LOG_ERR(TAG, "%s: true frame is null \n", __func__);
	}
	IMP_LOG_VERBOSE(TAG, "get cluster:%d \n", buf.index);

	return 0;
}

static int release_Frame(IMPFrameInfo *frame, void *pri)
{
#if 1
	Framesource *framesource = (Framesource *)pri;
	FSChannel *channel = &framesource->channel[frame->pool_idx];
	struct v4l2_buffer buf;
	memset(&buf, 0, sizeof(struct v4l2_buffer));

	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_USERPTR;
	buf.index = frame->index;
	if (((0 == channel->index) && (1 == channel->direct_mode)) ||
			(((0 == channel->index) || (3 == channel->index)) && (2 == channel->direct_mode)) ||
				(((0 == channel->index) || (3 == channel->index) || (6 == channel->index)) && (3 == channel->direct_mode)))
	{
		buf.m.userptr = (unsigned long)frame->direct_phyAddr;
	} else {
		buf.m.userptr = (unsigned long)frame->phyAddr;
	}
	buf.length = frame->size;
	//IMP_LOG_DBG(TAG, "@-@ start %s[%d] poolid = %d index = %d size = %d @@\n",__func__,__LINE__, frame->pool_idx, buf.index, buf.length);
	if (-1 == ioctl (channel->fs_fd, VIDIOC_QBUF, &buf)) {
		IMP_LOG_ERR(TAG, "%s: VIDIOC_QBUF failed, poolid = %d index = %d\n", __func__, frame->pool_idx, buf.index);
		return -1;
	}

	channel->qframecnt++;
	//IMP_LOG_DBG(TAG, "@-@ end %s[%d] poolid = %d index = %d size = %d @@\n",__func__,__LINE__, frame->pool_idx, buf.index, buf.length);
	IMP_LOG_VERBOSE(TAG, "release cluster:%d \n", buf.index);
	((IMPFrameInfoPriv*)(frame->priv))->qcount++;
#endif

	return 0;
}

static int get_ext_channel_empty_frame_index(FSChannel *ext_chan)
{
	int i;

	for (i = 0; i < ext_chan->ext_buffer->nr_buffers; i++) {
		IMPFrameInfo *frame_t = ext_chan->ext_buffer->frames[i];
		if (frame_t == NULL)
			return i;
	}

	return -1;
}

static int ext_channel_get_frame(IMPFrameInfo *frame, void *pri)
{
	Framesource *framesource = (Framesource *)pri;
	FSChannel *channel = &framesource->channel[frame->pool_idx];
	int i;

	for (i = 0; i < channel->ext_buffer->nr_buffers; i++) {
		IMPFrameInfo *frame_t = channel->ext_buffer->frames[i];
		if (frame_t) {
			*frame = *frame_t;
			return 0;
		}
	}

	return -1;
}

static int ext_channel_release_frame(IMPFrameInfo *frame, void *pri)
{
	Framesource *framesource = (Framesource *)pri;
	FSChannel *channel = &framesource->channel[frame->pool_idx];

	channel->ext_buffer->frames[frame->index] = NULL;

	return 0;
}


static void *frame_pooling_thread(void *p)
{
	FSChannel *channel = (FSChannel *)p;
	char thread_name[64];

	sprintf(thread_name, "FS(%d)-tick", channel->index);
	prctl(PR_SET_NAME, (unsigned long)thread_name);

	while (1) {
		IMP_LOG_VERBOSE(TAG, "Tick Channel-%d\n", channel->index);
		pthread_testcancel();
		int ready_bufs = -1;
		int ret = ioctl(channel->fs_fd, VIDIOC_DEFAULT_CMD_LISTEN_BUF, &ready_bufs);
		if (ret < 0) {
			IMP_LOG_INFO(TAG, "%s(%d) ready_bufs = %d\n", __func__, __LINE__, ready_bufs);
			continue;
		}
		Group *group = channel->group;
		group_tick(group);
	}

	return NULL;
}

static Framesource *alloc_framesource(void)
{
	Framesource *framesource;
	Device *dev = alloc_device("Framesource", sizeof(Framesource));

	if (dev == NULL) {
		IMP_LOG_ERR(TAG, "alloc_device() error\n");
		return NULL;
	}
	dev->nr_groups = NR_MAX_FS_GROUPS;
	dev->dev_id = DEV_ID_FS;

	framesource = device_pri(dev);
	framesource->device = dev;

	return framesource;
}

static void free_framesource(Framesource *framesource)
{
	Device *dev = framesource->device;
	free_device(dev);
}

/* Move frame to a new offset start point. */
static int move_frame(IMPFrameInfo *frame, int x_off, int y_off)
{
	IMP_LOG_VERBOSE(TAG, "%s: offx=%d offy=%d\n", __func__, x_off, y_off);

	/* move vertical first */
	void *src_y;
	void *dest_y;
	int size_y;
	void *src_uv;
	void *dest_uv;
	int size_uv;
	int nr_lines_y_off = abs(y_off);
	int nr_lines_y_move = frame->height - nr_lines_y_off;

	if (y_off > 0) {
		src_y = (void *)frame->virAddr;
		dest_y = (void *)(frame->virAddr + frame->width * nr_lines_y_off);
		size_y = frame->width * frame->height - frame->width * nr_lines_y_off;
		src_uv = (void *)(frame->virAddr + frame->width * frame->height);
		dest_uv = (void *)(frame->virAddr + frame->width * frame->height + frame->width * nr_lines_y_off / 2);
		size_uv = size_y / 2;

		memmove(dest_y, src_y, size_y);
		memset(src_y, 0, frame->width * nr_lines_y_off);

		memmove(dest_uv, src_uv, size_uv);
		memset(src_uv, 128, frame->width * nr_lines_y_off / 2);

	} else if (y_off < 0) {
		src_y = (void *)(frame->virAddr + frame->width * nr_lines_y_off);
		dest_y = (void *)frame->virAddr;
		size_y = frame->width * frame->height - frame->width * nr_lines_y_off;
		src_uv = (void *)(frame->virAddr + frame->width * frame->height + frame->width * nr_lines_y_off / 2);
		dest_uv = (void *)(frame->virAddr + frame->width * frame->height);
		size_uv = size_y / 2;

		memmove(dest_y, src_y, size_y);
		memset((void *)(frame->virAddr + size_y), 0, frame->width * nr_lines_y_off);

		memmove(dest_uv, src_uv, size_uv);
		memset((void *)(frame->virAddr + frame->width * frame->height + size_uv), 128, frame->width * abs(y_off) / 2);
	} else {
		dest_y = (void *)frame->virAddr;
		dest_uv = (void *)(frame->virAddr + frame->width * frame->height);
	}

	if (x_off) {
		int nr_pix_x_off = abs(x_off);
		int nr_pix_x_move = frame->width - nr_pix_x_off;

		void *src_y_hor;
		void *dest_y_hor;
		void *src_uv_hor;
		void *dest_uv_hor;

		int i;
		for (i = 0; i < nr_lines_y_move; i++) {
			void *line_start_y = (void *)((int)dest_y + i * frame->width);
			if (x_off > 0) {
				src_y_hor = line_start_y;
				dest_y_hor = (void *)((int)line_start_y + nr_pix_x_off);

				memmove(dest_y_hor, src_y_hor, nr_pix_x_move);
				memset(src_y_hor, 0, nr_pix_x_off);
			} else if (x_off < 0) {
				src_y_hor = (void *)((int)line_start_y + nr_pix_x_off);
				dest_y_hor = line_start_y;

				memmove(dest_y_hor, src_y_hor, nr_pix_x_move);
				memset((void *)((int)dest_y_hor + nr_pix_x_move), 0, nr_pix_x_off);
			}
		}

		for (i = 0; i < nr_lines_y_move / 2; i++) {
			void *line_start_uv = (void *)((int)dest_uv + i * frame->width);
			if (x_off > 0) {
				src_uv_hor = line_start_uv;
				dest_uv_hor = (void *)((int)line_start_uv + nr_pix_x_off);

				memmove(dest_uv_hor, src_uv_hor, nr_pix_x_move);
				memset(src_uv_hor, 128, nr_pix_x_off);
			} else if (x_off < 0) {
				src_uv_hor = (void *)((int)line_start_uv + nr_pix_x_off);
				dest_uv_hor = line_start_uv;

				memmove(dest_uv_hor, src_uv_hor, nr_pix_x_move);
				memset((void *)((int)dest_uv_hor + nr_pix_x_move), 128, nr_pix_x_off);
			}
		}
	}

	return 0;
}

typedef struct {
	uint32_t en[NR_MAX_FS_CHNS];
	uint32_t cnt[NR_MAX_FS_CHNS];
} dbg_fs_snap_yuv;

dbg_fs_snap_yuv g_dbg_fs_snap_yuv;


extern int ipu_csc_nv12_to_hsv(uint8_t *psrc, uint8_t *pdst, uint32_t width, uint32_t height);
extern int ipu_csc_nv12_to_nv21(uint8_t *psrc, uint8_t *pdst, uint32_t width, uint32_t height);
extern int ipu_csc_nv12_to_bgra(uint8_t *psrc, uint8_t *pdst, uint32_t width, uint32_t height);
int nv12_copyto_yuyv422(uint8_t* src, uint8_t* dst, uint32_t width, uint32_t height)
{
	int i,j;
	uint32_t w = width;
	uint32_t h = height;
	uint8_t *src_uv_plane = src+w*((h+15)&~15);
	uint8_t *src_y = src;
	uint8_t *dst_ = dst;
	uint8_t *src_uv;

	for (i = 0; i < h; i++) {
		src_uv = src_uv_plane+(i/2)*w;
		for (j = 0; j < w/2; j++) {
			*dst_ = *src_y;
			dst_++;
			src_y++;
			*dst_ = *src_uv;
			dst_++;
			src_uv++;
			*dst_ = *src_y;
			dst_++;
			src_y++;
			*dst_ = *src_uv;
			dst_++;
			src_uv++;
		}
	}
	return 0;
}

extern int _jz_fpsDrop[];
static int on_framesource_group_data_update(Group *group, IMPFrameInfo *d)
{
	int ret;
	int i = 0;
	int maxdelay = 0;
	int delay = 0;

	void *src_y;
	void *dest_y;
	void *src_uv;
	void *dest_uv;

    uint8_t ratio_w = 0;
    uint8_t ratio_h = 0;
    uint8_t pad_en = 0;

    FSChannel *channel;
	IMPFrameInfo *frame = NULL;
	Device *dev = get_device_of_group(group);
	Framesource *framesource = (Framesource *)device_pri(dev);
	channel = &(framesource->channel[group->group_index]);
#ifdef SHOW_DENOISE_TIME
	int64_t start_time_raw = 0, end_time_raw = 0;
	int64_t start_time_thread = 0, end_time_thread = 0;
#endif

    IMP_LOG_VERBOSE(TAG, "[%s][%s] update\n",
			framesource->device->name, group->module->name);
	pthread_mutex_lock(&channel->mutex);
	maxdelay = channel->fifo_frame_maxcnt;
	delay = channel->fifo_frame_cnt;
	pthread_mutex_unlock(&channel->mutex);
	if (maxdelay) {
		void *fifo = channel->fifo_frame;
		uint32_t data;
		int32_t cnt;
		cnt = fifo_num(fifo);
		if (cnt < 0) {
			IMP_LOG_ERR(TAG, "fifo_num error:%d, group->group_index = %d\n", cnt, group->group_index);
			return -1;
		}

		if(channel->fifo_type == FIFO_CACHE_PRIORITY){
			if (cnt < delay) {
				ret = VBMGetFrame(group->group_index, &frame);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "VBMGetFrame() error:%d, group->group_index = %d\n", ret, group->group_index);
					return -1;
				}
				ret = fifo_put(fifo, (uint32_t*)&frame);
				if (0 != ret) {
					IMP_LOG_ERR(TAG, "fifo_put error:%d, group->group_index = %d\n", ret, group->group_index);
					return -1;
				}
				if ((channel->wait_frame_flag)&&(channel->wait_frame_ts < frame->timeStamp)) {
					pthread_cond_signal(&(channel->cond_timedframe));
				}

				int num_of_observer = group->module->num_of_observer;
				for (i = 0; i < num_of_observer; i++){
					group->for_output_data[i] = NULL;
				}
				return 0;
			} else if (cnt == delay){
				ret = VBMGetFrame(group->group_index, &frame);
				if (ret < 0) {
					IMP_LOG_ERR(TAG, "VBMGetFrame() error:%d, group->group_index = %d\n", ret, group->group_index);
					return -1;
				}
				ret = fifo_get(fifo, &data);
				if (0 != ret) {
					IMP_LOG_ERR(TAG, "fifo_get error:%d, group->group_index = %d\n", ret, group->group_index);
					return -1;
				}
				ret = fifo_put(fifo, (uint32_t*)&frame);
				if (0 != ret) {
					IMP_LOG_ERR(TAG, "fifo_put error:%d, group->group_index = %d\n", ret, group->group_index);
					return -1;
				}
				if ((channel->wait_frame_flag)&&(channel->wait_frame_ts < frame->timeStamp)) {
					pthread_cond_signal(&(channel->cond_timedframe));
				}
				frame = (IMPFrameInfo*)data;
			} else {
				ret = fifo_get(fifo, &data);
				if (0 != ret) {
					IMP_LOG_ERR(TAG, "fifo_get error:%d, group->group_index = %d\n", ret, group->group_index);
					return -1;
				}
				frame = (IMPFrameInfo*)data;
			}
		}else if(channel->fifo_type == FIFO_DATA_PRIORITY){
			ret = VBMGetFrame(group->group_index, &frame);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "VBMGetFrame() error:%d, group->group_index = %d\n", ret, group->group_index);
				return -1;
			}
			/*printf("cnt = %d, delay = %d\n", cnt, delay);	*/
			while(cnt >= delay){
				ret = fifo_get(fifo, &data);
				if (0 != ret) {
					IMP_LOG_ERR(TAG, "fifo_get error:%d, group->group_index = %d\n", ret, group->group_index);
					return -1;
				}
				VBMUnLockFrame((IMPFrameInfo*)data);
				cnt--;
			}
			ret = fifo_put(fifo, (uint32_t*)&frame);
			if (0 != ret) {
				IMP_LOG_ERR(TAG, "fifo_put error:%d, group->group_index = %d\n", ret, group->group_index);
				return -1;
			}
			VBMLockFrame(frame);
			if ((channel->wait_frame_flag)&&(channel->wait_frame_ts < frame->timeStamp)) {
				pthread_cond_signal(&(channel->cond_timedframe));
			}
		}else{
			IMP_LOG_ERR(TAG, "fifo_type is wrong!(val = %d)\n", channel->fifo_type);
		}
	} else {
		ret = VBMGetFrame(group->group_index, &frame);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "VBMGetFrame() error:%d, group->group_index = %d\n", ret, group->group_index);
			return -1;
		}
	}

	//snap one frame
	pthread_mutex_lock(&channel->mutex);
	if (STATUS_SNAPFRAME_START == channel->status_snapframe) {
		uint8_t * outbuf = channel->pbuf_snapframe;
		if (!(((0 == channel->index) && (1 == channel->direct_mode)) ||
				(((0 == channel->index) || (3 == channel->index)) && (2 == channel->direct_mode)) ||
					(((0 == channel->index) || (3 == channel->index) || (6 == channel->index)) && (3 == channel->direct_mode))))
		{
			if ((PIX_FMT_NV12 == channel->attr.pixFmt)&&(PIX_FMT_YUYV422 == channel->fmt_snapframe)) {
				//do nv12 to yuyv422 copy
				ret = nv12_copyto_yuyv422((uint8_t*)frame->virAddr, (uint8_t*)outbuf, frame->width, frame->height);
				if (0 != ret) {
					return -1;
				}
			} else if ((PIX_FMT_NV12 == channel->attr.pixFmt)&&(PIX_FMT_NV12 == channel->fmt_snapframe)) {
				memcpy((void *)outbuf, (void *)frame->virAddr, frame->width * frame->height);
				memcpy((void *)outbuf + frame->width * frame->height,
						(void *)frame->virAddr + frame->width * ((frame->height + 15) & ~15),
						frame->width * frame->height / 2);
			} else if ((PIX_FMT_RGBA == channel->attr.pixFmt)&&(PIX_FMT_RGBA == channel->fmt_snapframe))
				memcpy((void *)outbuf, (void *)frame->virAddr, frame->width * frame->height * 4);
			if (channel->info_snapframe)
				memcpy(channel->info_snapframe, frame, sizeof(IMPFrameInfo));
			channel->status_snapframe = STATUS_SNAPFRAME_DONE;
			pthread_cond_signal(&channel->cond_snapframe);
		}
	}
	pthread_mutex_unlock(&channel->mutex);

	pthread_mutex_lock(&channel->mutex);
	if (channel->depth > 0) {
		struct FSDepth *fsdepth = NULL;
		IMPFrameInfo *frame_bak = NULL;
		uint32_t frame_bak_vaddr = 0;
		uint32_t frame_bak_paddr = 0;
		uint32_t frame_bak_size = 0;
#if TIME
		int timeout;
		struct timeval timeval, timeval1;
		int64_t start_time_raw = 0, end_time_raw = 0;
		int64_t start_time_thread = 0, end_time_thread = 0;
#endif
		if(channel->list_prepare && (channel->list_prepare->prev != channel->list_prepare)) {
			fsdepth = channel->list_prepare->prev;
		} else if (channel->list_done && (channel->list_done->prev != channel->list_done)) {
			fsdepth = channel->list_done->prev;
		}

		if (fsdepth) {
			list_del_FSDepth(fsdepth);
			frame_bak = fsdepth->frame;
#if TIME
			gettimeofday(&timeval, NULL);
			start_time_raw = system_gettime(RAW);
			start_time_thread = system_gettime(THREAD);
#endif
			frame_bak_vaddr = frame_bak->virAddr;
			frame_bak_paddr = frame_bak->phyAddr;
			frame_bak_size = frame_bak->size;
            memcpy(frame_bak, frame, sizeof(IMPFrameInfo));
			frame_bak->virAddr = frame_bak_vaddr;
			frame_bak->phyAddr = frame_bak_paddr;
	        frame_bak->size = frame_bak_size;

			if (!(((0 == channel->index) && (1 == channel->direct_mode)) ||
					(((0 == channel->index) || (3 == channel->index)) && (2 == channel->direct_mode)) ||
						(((0 == channel->index) || (3 == channel->index) || (6 == channel->index)) && (3 == channel->direct_mode))))
			{
				if (frame->pixfmt == V4L2_PIX_FMT_NV12 || frame->pixfmt == V4L2_PIX_FMT_NV21) {
					memcpy((void *)frame_bak->virAddr, (void *)frame->virAddr, frame->width * frame->height);
					memcpy((void *)frame_bak->virAddr + frame->width * frame->height,
							(void *)frame->virAddr + frame->width * ((frame->height + 15) & ~15),
							frame->width * frame->height / 2);
					frame_bak->size = frame->width * frame->height * 3 / 2;
				} else if (frame->pixfmt == PIX_FMT_HSV) {
					uint8_t *src;
					uint8_t *dst;
					uint32_t size;
					//copy h
					dst = (uint8_t*)frame_bak->virAddr;
					src = (uint8_t*)frame->virAddr;
					size = frame->width*frame->height*2;
					memcpy(dst, src, size);
					//copy s
					dst += size;
					src += frame->width*((frame->height + 15) & ~15)*2;
					size = frame->width*frame->height;
					memcpy(dst, src, size);
					//copy v
					dst += size;
					src += frame->width*((frame->height + 15) & ~15);
					size = frame->width*frame->height;
					memcpy(dst, src, size);
					frame_bak->size = frame->width * frame->height * 4;
				} else if (frame->pixfmt == PIX_FMT_RGBA){
					uint8_t *src_rgba;
					uint8_t *dst_rgba;
					uint32_t size_rgba;
					dst_rgba = (uint8_t*)frame_bak->virAddr;
					src_rgba = (uint8_t*)frame->virAddr;
					//size_rgba = frame->width * ((frame->height+15) & ~15)*4;
					size_rgba = frame->width * frame->height * 4;
					memcpy(dst_rgba, src_rgba, size_rgba);
					frame_bak->size = frame->width * frame->height * 4;
				} else {
					memcpy((void *)frame_bak->virAddr, (void *)frame->virAddr, frame->size);
				}
			}
#if TIME
			end_time_raw = system_gettime(RAW);
			end_time_thread = system_gettime(THREAD);
			gettimeofday(&timeval1, NULL);
#endif
			list_add_head(fsdepth, channel->list_done);
#if TIME
			IMP_LOG_DBG(TAG, "RAW : %llu\n", end_time_raw- start_time_raw);
			IMP_LOG_DBG(TAG, "THREAD : %llu\n", end_time_thread- start_time_thread);
			timeout = (timeval1.tv_sec - timeval.tv_sec) * 1000000 + (timeval1.tv_usec - timeval.tv_usec);
			IMP_LOG_DBG(TAG, "\n\ntimeout : %d\n", timeout);
#endif
			pthread_cond_signal(&(channel->cond));
		}
	}
	pthread_mutex_unlock(&(channel->mutex));

	if (frame->pixfmt == V4L2_PIX_FMT_NV12 || frame->pixfmt == V4L2_PIX_FMT_NV21) {
		{
			if (!(((0 == channel->index) && (1 == channel->direct_mode)) ||
					(((0 == channel->index) || (3 == channel->index)) && (2 == channel->direct_mode)) ||
						(((0 == channel->index) || (3 == channel->index) || (6 == channel->index)) && (3 == channel->direct_mode))))
			{
				char access_name[50];
				sprintf(access_name, "/tmp/mountdir/ispsnap%d.nv12", group->group_index);
				if (access(access_name, R_OK | W_OK) == 0) {
					struct stat stat_buf;
					stat(access_name, &stat_buf);
					int ispsnapsize = frame->width * frame->height * 3 / 2;
					if (stat_buf.st_size < ispsnapsize) {
						int ispsnapfd = open(access_name, O_RDWR | O_APPEND);
						if (ispsnapfd >= 0) {
							write(ispsnapfd, (void *)frame->virAddr, frame->width * frame->height);
							write(ispsnapfd, (void *)frame->virAddr + frame->width * ((frame->height + 15) & ~15),
									frame->width * frame->height / 2);
							close(ispsnapfd);
						}
					}
				}
				if (0 != g_dbg_fs_snap_yuv.en[group->group_index]) {
					if (g_dbg_fs_snap_yuv.cnt[group->group_index] > 0) {
						printf("info: isp snap ch-%d, index-%d\n", group->group_index, g_dbg_fs_snap_yuv.cnt[group->group_index]);
						sprintf(access_name, "/tmp/ispsnap-%d.nv12", group->group_index);
						int ispsnapfd = open(access_name, O_RDWR | O_APPEND | O_CREAT);
						if (ispsnapfd >= 0) {
							write(ispsnapfd, (void *)frame->virAddr, frame->width * frame->height);
							write(ispsnapfd, (void *)frame->virAddr + frame->width * ((frame->height + 15) & ~15),
									frame->width * frame->height / 2);
							close(ispsnapfd);
						} else {
							printf("error: open %s\n", strerror(errno));
						}
						g_dbg_fs_snap_yuv.cnt[group->group_index]--;
					} else {
						g_dbg_fs_snap_yuv.en[group->group_index] = 0;
					}
				}
			}
		}

		if (frame->height % 16) {
			int realFrameHeight = ((frame->height + 15) & ~15);
			int deltaFrameHeight = realFrameHeight - frame->height;
			/* pad virtical direction */
			if (!(((0 == channel->index) && (1 == channel->direct_mode)) ||
					(((0 == channel->index) || (3 == channel->index)) && (2 == channel->direct_mode)) ||
						(((0 == channel->index) || (3 == channel->index) || (6 == channel->index)) && (3 == channel->direct_mode))))
			{
				if (deltaFrameHeight > 0) {
					uint32_t base = frame->virAddr + frame->width * frame->height;
					for (i = 0; i < deltaFrameHeight; i++) {
						memcpy((void *)(base + i * frame->width), (void *)(base - frame->width), frame->width);
					}
					base = frame->virAddr + frame->width * (realFrameHeight + frame->height / 2);
					for (i = 0; i < deltaFrameHeight / 2; i++) {
						memcpy((void *)(base + i * frame->width), (void *)(base - frame->width), frame->width);
					}
				}
			}
		}

		/* Copy Frame to extend channel. */
		if (group->group_index == 0) { /* Extend channel is copied from main channel. */
			if (!(((0 == channel->index) && (1 == channel->direct_mode)) ||
					(((0 == channel->index) || (3 == channel->index)) && (2 == channel->direct_mode)) ||
						(((0 == channel->index) || (3 == channel->index) || (6 == channel->index)) && (3 == channel->direct_mode))))
			{
				for (i = 1; i < NR_MAX_FS_CHNS; i++) {
					FSChannel *chan = &framesource->channel[i];
					if ((chan->state == FSCHANNEL_STATE_RUN) && (chan->attr.type == FS_EXT_CHANNEL)) {
						sem_wait(&chan->ext_sync_sem);
					}
					if ((chan->state == FSCHANNEL_STATE_RUN) && (chan->attr.type == FS_EXT_CHANNEL)) {
						int frame_index = get_ext_channel_empty_frame_index(chan);
						if (frame_index >= 0) {
							IMPFrameInfo *frame_ext = VBMGetFrameInstance(chan->index, frame_index);

							if (sw_resize_use_simd) {
								/* Insert MXU key first. */
								mk();
							}
							frame_ext->size = calc_pic_size(chan->attr.picWidth,
									((chan->attr.picHeight + 15) & ~15),
									chan->attr.pixFmt);
							//DO convert

							if (PIX_FMT_YUYV422 == chan->attr.pixFmt) {
								if ((frame->width == frame_ext->width)&&(frame->height == frame_ext->height)) {
									//do nv12 to yuyv422 copy
									nv12_copyto_yuyv422((uint8_t*)frame->virAddr, (uint8_t*)frame_ext->virAddr, frame_ext->width, ((frame_ext->height + 15) & ~15));
								} else {
									printf("err: extfs yuyv422 resolution should be same to source\n");
								}

							} else if (PIX_FMT_HSV == chan->attr.pixFmt) {
#if 0
								IMP_LOG_ERR(TAG, "chan->index = %d\n",  chan->index);
								IMP_LOG_ERR(TAG, "virAddr = %p, resize_frame_buf = %p frame->width = %d, frame->height = %d, frame_ext->width = %d, frame_ext->height = %d\n",
										frame->virAddr, chan->ext_buffer->resize_frame_buf, frame->width, frame->height, frame_ext->width, frame_ext->height);
#endif
#if 1
								IMPAlloc *alloc = chan->ext_buffer->resize_frame_buf;
								if ((frame->width == frame_ext->width)&&(frame->height == frame_ext->height)) {
									//do nv12 to hsv convert
									ipu_csc_nv12_to_hsv((uint8_t*)frame->phyAddr, (uint8_t*)frame_ext->phyAddr, frame_ext->width, ((frame_ext->height + 15) & ~15));
								} else {
									if (0 == alloc->info.vaddr) {
										uint32_t size = calc_pic_size(frame_ext->width, ((frame_ext->height + 15) & ~15), PIX_FMT_NV12);
										IMP_Alloc(alloc, size, user);
									}
									sw_resize((uint8_t*)frame->virAddr, (uint8_t*)alloc->info.vaddr,
											frame->width, ((frame->height + 15) & ~15),
											frame_ext->width, ((frame_ext->height + 15) & ~15),
											chan->attr.crop.enable,
											chan->attr.crop.top * frame->width + chan->attr.crop.left,
											chan->attr.crop.width, ((chan->attr.crop.height + 15) & ~15), 0,
											(uint16_t *)chan->ext_buffer->resize_tmp_buf);
									//do nv12 to hsv convert
									ipu_csc_nv12_to_hsv((uint8_t*)alloc->info.paddr, (uint8_t*)frame_ext->phyAddr, frame_ext->width, ((frame_ext->height + 15) & ~15));
								}
#endif
								//IMP_LOG_ERR(TAG, "111#### resize_frame_buf = %p\n", chan->ext_buffer->resize_frame_buf);
							} else if (PIX_FMT_RGBA == chan->attr.pixFmt) {
#if 1
								IMPAlloc *alloc = chan->ext_buffer->resize_frame_buf;
								if ((frame->width == frame_ext->width)&&(frame->height == frame_ext->height)) {
									//do nv12 to brga convert
									ipu_csc_nv12_to_bgra((uint8_t*)frame->phyAddr, (uint8_t*)frame_ext->phyAddr, frame_ext->width, ((frame_ext->height + 15) & ~15));
								} else {
									if (0 == alloc->info.vaddr) {
										uint32_t size_rgba = calc_pic_size(frame_ext->width, ((frame_ext->height + 15) & ~15), PIX_FMT_NV12);
										IMP_Alloc(alloc, size_rgba, user);
									}
									sw_resize((uint8_t*)frame->virAddr, (uint8_t*)alloc->info.vaddr,
											frame->width, ((frame->height + 15) & ~15),
											frame_ext->width, ((frame_ext->height + 15) & ~15),
											chan->attr.crop.enable,
											chan->attr.crop.top * frame->width + chan->attr.crop.left,
											chan->attr.crop.width, ((chan->attr.crop.height + 15) & ~15), 0,
											(uint16_t *)chan->ext_buffer->resize_tmp_buf);
									//do nv12 to rgba convert
									ipu_csc_nv12_to_bgra((uint8_t*)alloc->info.paddr, (uint8_t*)frame_ext->phyAddr, frame_ext->width, ((frame_ext->height + 15) & ~15));
								}
#endif
							} else {

								src_y = (void *)frame->virAddr;
								dest_y = (void *)frame_ext->virAddr;

								src_uv = (void *)(frame->virAddr + frame->width * ((frame->height + 15)&~15));
								dest_uv = (void *)(frame_ext->virAddr + frame_ext->width * ((frame_ext->height + 15)&~15));

#if 0    /* scaler_8 */
								ratio_w = (frame->width*16)/frame_ext->width;
								ratio_h = (frame->height*16)/frame_ext->height;
#else   /* scaler_8_v3 & scaler_8_v4 */
								ratio_w = (frame->width)/frame_ext->width;
								ratio_h = (frame->height)/frame_ext->height;

#endif
								if (frame_ext->height%16 != 0) {
									pad_en = 1;
								} else {
									pad_en = 0;
								}

								/*init*/
								int sstep = frame->width;
								int row_index_len = sizeof(uint32_t) * 3 * frame_ext->height;
								int col_index_len = sizeof(uint32_t) * frame_ext->width;
								uint32_t* row_index = 0;
								if (0 == chan->pbuf_resize) {
									chan->pbuf_resize = (uint32_t*)malloc(row_index_len + col_index_len);
								}
								row_index = chan->pbuf_resize;
								uint32_t* col_index = (uint32_t*)((uint8_t*)row_index + row_index_len);
								if(!row_index)
								{
									printf("Error: malloc failed\n");
									return -1;
								}
								nv12_scaler_8_init(frame->width, frame->height, frame_ext->width, frame_ext->height, sstep, row_index, col_index);

								nv12_scaler_8_v3((uint8_t*)src_y, (uint8_t*)dest_y, (uint8_t*)src_uv, (uint8_t*)dest_uv, frame->width, frame->height, frame_ext->width, frame_ext->height, row_index, col_index, pad_en);

								/*free*/
#if 0
								if(row_index){
									free(row_index);
									row_index = NULL;
								}
#endif

#if 0
								sw_resize((uint8_t*)frame->virAddr, (uint8_t*)frame_ext->virAddr,
										frame->width, ((frame->height + 15) & ~15),
										frame_ext->width, ((frame_ext->height + 15) & ~15),
										chan->attr.crop.enable,
										chan->attr.crop.top * frame->width + chan->attr.crop.left,
										chan->attr.crop.width, ((chan->attr.crop.height + 15) & ~15), 0,
										(uint16_t *)chan->ext_buffer->resize_tmp_buf);
#endif
							}
							frame_ext->index = frame_index;
							frame_ext->size = calc_pic_size(chan->attr.picWidth,
									((chan->attr.picHeight + 15) & ~15),
									chan->attr.pixFmt);
							frame_ext->timeStamp = frame->timeStamp;
							chan->ext_buffer->frames[frame_index] = frame_ext;
							group_tick(chan->group);
						}
					}
					if ((chan->state == FSCHANNEL_STATE_RUN) && (chan->attr.type == FS_EXT_CHANNEL)) {
						sem_post(&chan->ext_sync_sem);
					}
				}
			}
		}

		/* Move frame to a new offset. Precision=2 pixel. */
		if (!(((0 == channel->index) && (1 == channel->direct_mode)) ||
				(((0 == channel->index) || (3 == channel->index)) && (2 == channel->direct_mode)) ||
					(((0 == channel->index) || (3 == channel->index) || (6 == channel->index)) && (3 == channel->direct_mode))))
		{
			if (channel->x_offset || channel->y_offset)
				move_frame(frame, channel->x_offset, channel->y_offset);
		}

		IMPFrameInfoPriv *frame_priv = frameInfo_pri(frame);
		if (_jz_fpsDrop[channel->index] > 0) {
			frame_priv->i_fps_num = framesource->inFrmRateNum - (_jz_fpsDrop[channel->index] * framesource->inFrmRateDen);
		} else {
			frame_priv->i_fps_num = framesource->inFrmRateNum;
		}
		frame_priv->i_fps_den = framesource->inFrmRateDen;
		if (framesource->is_changwait_cnt > 0) {
			framesource->is_changwait_cnt--;
			frame_priv->b_use_ncuinfo = 0;
		}

		/* distortion correction */
		pthread_mutex_lock(&channel->mutex);
		if ((1 == channel->distortion.en)&&(NULL != channel->distortion.handle)) {
			if (NULL == channel->distortion.framebuf1) {
				channel->distortion.framebuf1 = frame;
				pthread_mutex_unlock(&channel->mutex);
				return 0;
			} else {
				undistort_handle *h = channel->distortion.handle;
				IMPFrameInfo *src,*dst;
				IMPFrameInfo *tmp;
				src = frame;
				dst = channel->distortion.framebuf1;

				if (!(((0 == channel->index) && (1 == channel->direct_mode)) ||
						(((0 == channel->index) || (3 == channel->index)) && (2 == channel->direct_mode)) ||
							(((0 == channel->index) || (3 == channel->index) || (6 == channel->index)) && (3 == channel->direct_mode))))
				{
					h->Fisheye_undistort((void*)src->virAddr, src->width, (void*)dst->virAddr, ((fisheye_param*)(h->init_handle))->map.xy, ((fisheye_param*)(h->init_handle))->map.fxy, ((fisheye_param*)(h->init_handle))->wtab,
							src->width, src->height, channel->distortion.mode);

					tmp = channel->distortion.framebuf1;
					channel->distortion.framebuf1 = frame;
					frame = tmp;
				}
			}
		} else {
			if (NULL != channel->distortion.framebuf1) {
				VBMReleaseFrame(channel->distortion.framebuf1);
			}
		}
		pthread_mutex_unlock(&channel->mutex);
	}

	int num_of_observer = group->module->num_of_observer;
	for (i = 0; i < num_of_observer; i++){
		group->for_output_data[i] = frame;
		VBMLockFrame(frame);
	}

	if (num_of_observer == 0) {
		VBMLockFrame(frame);
		VBMUnLockFrame(frame);
		return -1;
	}

	return 0;
}

static int framesource_create_group(int group_index)
{
	Framesource *framesource = get_framesource();
	Device *dev = framesource->device;

	if (group_index > NR_MAX_FS_GROUPS - 1) {
		IMP_LOG_ERR(TAG, "Invalid group num%d\n", group_index);
		return -1;
	}

	char grp_name[MAX_MODULE_NAME_LEN];
	sprintf(grp_name, "%s-%d", dev->name, group_index);

	Group *grp = create_group(DEV_ID_FS, group_index, grp_name,
							  on_framesource_group_data_update);
	grp->device = dev;
	grp->nr_outputs = NR_MAX_OUTPUT_IN_GROUP; /* Fixed */
	dev->groups[group_index] = grp;

	return 0;
}

static int framesource_destroy_group(int group_index)
{
	Framesource *framesource = get_framesource();
	Device *dev = framesource->device;

	if (group_index > NR_MAX_FS_GROUPS - 1) {
		IMP_LOG_ERR(TAG, "Invalid group num%d\n\n", group_index);
		return -1;
	}

	Group *grp = dev->groups[group_index];

	if (grp == NULL) {
		IMP_LOG_WARN(TAG, "group-%d has not been created\n", group_index);
		return -1;
	}

	destroy_group(grp, DEV_ID_FS); /* TODO return value. */
	dev->groups[group_index] = NULL;

	return 0;
}

int FrameSourceInit(void)
{
	int ret = 0;

	if(gFramesource){
		return 0;
	}

	gFramesource = alloc_framesource();
	if (gFramesource == NULL){
		IMP_LOG_ERR(TAG, "Failed to alloc framesource!\n");
		return -1;
	}

	if (gFramesource != NULL)
		gFramesource->state = FSCHANNEL_STATE_OPEN;

	if (is_has_simd128()) {
        assert(0);
#if 0
		sw_resize_use_simd = 1;
		sw_resize = c_resize_simd;
#endif
	} else {
		sw_resize_use_simd = 0;
		sw_resize = c_resize_c;
	}

	int dbg_fs_info(void *buf, int msize, void **arg);
	dsys_func_share_mem_register(FUNC_MODULE_FS, DBG_FS_INFO, "fs_info", dbg_fs_info);
	int dbg_fs_ivdc_s(void *buf, int msize, void **arg);
	dsys_func_share_mem_register(FUNC_MODULE_FS, DBG_FS_IVDC_S, "fs_ivdc_s", dbg_fs_ivdc_s);

	int dbg_misc_save_pic(umem_info *uinfo, void **pri);
	dsys_func_user_mem_register(FUNC_MODULE_MISC, DBG_MISC_SAVE_PIC, "misc_save_pic", dbg_misc_save_pic, 0);
	int dbg_misc_system_info(void *buf, int msize, void **arg);
	dsys_func_share_mem_register(FUNC_MODULE_MISC, DBG_MISC_SYSTEM_INFO, "misc_system_info", dbg_misc_system_info);
	int dbg_misc_simple_cmd(void *buf, int msize, void **arg);
	dsys_func_share_mem_register(FUNC_MODULE_MISC, DBG_MISC_SIMPLE_CMD, "misc_simple_cmd", dbg_misc_simple_cmd);
	return ret;
}

int FrameSourceExit(void)
{
	int ret = 0;

	if(!gFramesource)
		return 0;

	dsys_func_unregister(FUNC_MODULE_FS, DBG_FS_INFO);
	dsys_func_unregister(FUNC_MODULE_FS, DBG_FS_IVDC_S);
	dsys_func_unregister(FUNC_MODULE_MISC, DBG_MISC_SAVE_PIC);
	dsys_func_unregister(FUNC_MODULE_MISC, DBG_MISC_SYSTEM_INFO);
	dsys_func_unregister(FUNC_MODULE_MISC, DBG_MISC_SIMPLE_CMD);
	if(gFramesource->state > FSCHANNEL_STATE_OPEN){
		IMP_LOG_ERR(TAG, "Failed to exit, because a Framechannel hasn't be destroy!");
		return -1;
	}

	free_framesource(gFramesource);
	gFramesource = NULL;

	return ret;
}

static void dump_framesource_chn_attr(int chnNum, const IMPFSChnAttr *chnAttr)
{
	IMP_LOG_DBG(TAG, "--------------------------------------------------------------\n");
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->picWidth=%d", chnNum, chnAttr->picWidth);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->picHeight=%d", chnNum, chnAttr->picHeight);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->pixFmt=%d", chnNum, chnAttr->pixFmt);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->crop.enable=%d", chnNum, chnAttr->crop.enable);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->crop.left=%d", chnNum, chnAttr->crop.left);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->crop.top=%d", chnNum, chnAttr->crop.top);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->crop.width=%d", chnNum, chnAttr->crop.width);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->crop.height=%d", chnNum, chnAttr->crop.height);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->scaler.enable=%d", chnNum, chnAttr->scaler.enable);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->scaler.outwidth=%d", chnNum, chnAttr->scaler.outwidth);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->scaler.outheight=%d", chnNum, chnAttr->scaler.outheight);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->fcrop.enable=%d", chnNum, chnAttr->fcrop.enable);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->fcrop.left=%d", chnNum, chnAttr->fcrop.left);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->fcrop.top=%d", chnNum, chnAttr->fcrop.top);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->fcrop.width=%d", chnNum, chnAttr->fcrop.width);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->fcrop.height=%d", chnNum, chnAttr->fcrop.height);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->outFrmRateNum=%d", chnNum, chnAttr->outFrmRateNum);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->outFrmRateDen=%d", chnNum, chnAttr->outFrmRateDen);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->nrVBs=%d", chnNum, chnAttr->nrVBs);
	IMP_LOG_DBG(TAG, "fs[%d]chnAttr->type=%d", chnNum, chnAttr->type);
	IMP_LOG_DBG(TAG, "--------------------------------------------------------------\n");
}

static int check_framesource_chn_attr(int chnNum, const IMPFSChnAttr *chnAttr)
{
	if (chnAttr == NULL) {
		IMP_LOG_ERR(TAG, "%s(): chnNum=%d, chnAttr is NULL\n", __func__, chnNum);
		return -1;
	}

    if ((chnAttr->pixFmt != PIX_FMT_RAW) && ((chnAttr->picWidth % 16) || (chnAttr->picHeight % 8))) {
        IMP_LOG_ERR(TAG, "chnNum=%d, picWidth=%d should be align to 16 and picHeight=%d should align to 8\n", chnNum, chnAttr->picWidth, chnAttr->picHeight);
        return -1;
    }

    if ((chnAttr->picWidth < 16) || (chnAttr->picHeight < 8)) {
        IMP_LOG_ERR(TAG, "chnNum=%d, too small fs resolution(%dx%d)\n", chnNum, chnAttr->picWidth, chnAttr->picHeight);
        return -1;
    }

#if 0
    if (chnAttr->scaler.enable && (chnAttr->scaler.outwidth == sensor_width) && (chnAttr->scaler.outheight == sensor_height)) {
        IMP_LOG_ERR(TAG, "chnNum=%d scaler.enable should be 0 for scaler resolution equals to sensors\n", chnNum);
        return -1;
    }
#endif

    if (chnAttr->scaler.enable && ((chnAttr->crop.enable && ((chnAttr->scaler.outwidth%4 != 0) || (chnAttr->scaler.outheight%4 != 0)))
                || (!chnAttr->crop.enable && ((chnAttr->scaler.outwidth%16 != 0) || (chnAttr->scaler.outheight%8 != 0))))){
        IMP_LOG_ERR(TAG, "chnNum=%d, scaler.outwidth=%d must be multiples of %d, scaler.outheight=%d must be multiples of %d\n", chnNum,
                chnAttr->scaler.outwidth, chnAttr->crop.enable ? 16 : 4, chnAttr->scaler.outheight, chnAttr->crop.enable ? 8 : 4);
        return -1;
    }

    if (chnAttr->scaler.enable && !chnAttr->crop.enable && ((chnAttr->scaler.outwidth != chnAttr->picWidth) || (chnAttr->scaler.outheight != chnAttr->picHeight))) {
        IMP_LOG_ERR(TAG, "chnNum=%d, if scaler enable crop disable, (scaler.outwidth, scaler.outheight)=(%d,%d) must equal to (picWidth, picHeight)=(%d,%d)\n",
                chnNum, chnAttr->scaler.outwidth, chnAttr->scaler.outheight, chnAttr->picWidth, chnAttr->picHeight);
        return -1;
    }

    if (chnAttr->crop.enable && ((chnAttr->crop.left%2 != 0) || (chnAttr->crop.top%2 != 0))) {
        IMP_LOG_ERR(TAG, "chnNum=%d, if crop enble, crop.left=%d must be multiples of 2 and crop.top=%d must be multiples of 2\n",
                chnNum, chnAttr->crop.left, chnAttr->crop.top);
        return -1;
    }

    if (chnAttr->crop.enable && ((chnAttr->crop.width%16 != 0) || (chnAttr->crop.height%8 != 0))) {
        IMP_LOG_ERR(TAG, "chnNum=%d, if crop enble, crop.width=%d must be multiples of 16 and crop.height=%d must be multiples of 8\n",
                chnNum, chnAttr->crop.width, chnAttr->crop.height);
        return -1;
    }

    if (chnAttr->scaler.enable && chnAttr->crop.enable && (((chnAttr->crop.left + chnAttr->crop.width) > chnAttr->scaler.outwidth) || ((chnAttr->crop.top + chnAttr->crop.height) > chnAttr->scaler.outheight))) {
        IMP_LOG_ERR(TAG, "chnNum=%d, if crop and scaler enble, crop.left=%d+crop.width=%d>scaler.outwidth=%d or crop.top=%d+crop.height=%d>scaler.outheight=%d\n",
                chnNum, chnAttr->crop.left, chnAttr->crop.width, chnAttr->scaler.outwidth, chnAttr->crop.top, chnAttr->crop.height, chnAttr->scaler.outheight);
        return -1;
    }

    if ((chnAttr->crop.enable && ((chnAttr->crop.width != chnAttr->picWidth) || (chnAttr->crop.height != chnAttr->picHeight)))) {
        IMP_LOG_ERR(TAG, "chnNum=%d, if crop enble, (crop.width, crop.height)=(%d,%d) must equal to (picWidth, picHeight)=(%d,%d)\n",
                chnNum, chnAttr->crop.width, chnAttr->crop.height, chnAttr->picWidth, chnAttr->picHeight);
        return -1;
    }

    if (!chnAttr->scaler.enable && !chnAttr->crop.enable && ((chnAttr->picWidth != sensor_width) || (chnAttr->picHeight != sensor_height))) {
        IMP_LOG_DBG(TAG, "chnNum=%d,invalid picture resolution(%dx%d),but sensor resolution(%dx%d) when crop and scaler all disabled\n", chnNum, chnAttr->picWidth, chnAttr->picHeight, sensor_width, sensor_height);
    }

    return 0;
}

/* APIs */
int IMP_FrameSource_SetChnAttr(int chnNum, const IMPFSChnAttr *chnAttr)
{
	int ret = 0;

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

    /* checkout chnAttr invalid */
    ret = check_framesource_chn_attr(chnNum, chnAttr);
    if (ret < 0) {
        IMP_LOG_ERR(TAG, "%s:chnNum=%d, check_framesource_chn_attr failed\n", __func__, chnNum);
        return -1;
    }

	Framesource *framesource = get_framesource();
	if (framesource == NULL) {
		IMP_LOG_ERR(TAG, "%s():[chn%d] FrameSource is invalid,"
					"maybe system was not inited yet.\n", __func__, chnNum);
		return -1;
	}

	FSChannel *chan = &framesource->channel[chnNum];

	/* dynamic set channel parameters to a channel */
    if (chan /*&& (chnNum == 0)*/ && (chnAttr->type == FS_PHY_CHANNEL) && (chan->state == FSCHANNEL_STATE_RUN)) {
        int diffcnt = 0;
        struct frame_image_format format;

        memset(&format, 0, sizeof(struct frame_image_format));

        if ((chnAttr->picWidth != chan->attr.picWidth) || (chnAttr->picHeight != chan->attr.picHeight)) {
            IMP_LOG_ERR(TAG, "chnNum=%d, picWidth=%d, picHeight=%d must equal to old picwidth=%d and picHeight=%d\n", chnNum, chnAttr->picWidth, chnAttr->picHeight, chan->attr.picWidth, chan->attr.picHeight);
            return -1;
        }

        diffcnt += chnAttr->scaler.enable != chan->attr.scaler.enable ? 1 : 0;
        if (chnAttr->scaler.enable == chan->attr.scaler.enable) {
            diffcnt += chnAttr->scaler.outwidth != chan->attr.scaler.outwidth ? 1 : 0;
            diffcnt += chnAttr->scaler.outheight != chan->attr.scaler.outheight ? 1 : 0;
        }

        diffcnt += chnAttr->crop.enable != chan->attr.crop.enable ? 1 : 0;
        if (chnAttr->crop.enable == chan->attr.crop.enable) {
            diffcnt += chnAttr->crop.top != chan->attr.crop.top ? 1 : 0;
            diffcnt += chnAttr->crop.left != chan->attr.crop.left ? 1 : 0;
            diffcnt += chnAttr->crop.height != chan->attr.crop.height ? 1 : 0;
            diffcnt += chnAttr->crop.width != chan->attr.crop.width ? 1 : 0;
        }

        if (diffcnt == 0) {
            IMP_LOG_WARN(TAG, "chnNum=%d, no need to scale and crop\n", chnNum);
            return -1;
        }

        if (chnAttr->pixFmt == PIX_FMT_RAW) {
            if(chan->index != 0) {
                IMP_LOG_ERR(TAG, "RAW is only channel == 0 !\n");
                return -1;
            }
            format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            format.pix.field = V4L2_FIELD_ANY;
            if ((ret = ioctl(chan->fs_fd, VIDIOC_GET_FRAME_FORMAT, &format)) != 0) {
                printf("***** Failed to get format of fr! *****\n");
                return -1;
            }

            chan->attr.picWidth = format.pix.width;
            chan->attr.picHeight = format.pix.height;
            chan->attr.pixFmt = format.pix.pixelformat;
        } else if ((PIX_FMT_HSV == chnAttr->pixFmt) | (PIX_FMT_RGBA == chnAttr->pixFmt)) {
            IMP_LOG_ERR(TAG, "FS_PHY_CHANNEL do not support HSV == 0 !\n");
            return -1;
        } else {
            format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            format.pix.field = V4L2_FIELD_ANY;
            format.pix.width = chnAttr->picWidth;
            format.pix.height = chnAttr->picHeight;
            format.pix.pixelformat = imppixfmt_to_v4l2pixfmt(chnAttr->pixFmt);
            format.crop_enable = chnAttr->crop.enable;
            format.crop_top = chnAttr->crop.top;
            format.crop_left = chnAttr->crop.left;
            format.crop_width = chnAttr->crop.width;
            format.crop_height = chnAttr->crop.height;

            format.scaler_enable = chnAttr->scaler.enable;
            format.scaler_out_width = chnAttr->scaler.outwidth;
            format.scaler_out_height = chnAttr->scaler.outheight;
        }

        chan->attr.picHeight = chnAttr->picHeight;
        chan->attr.picWidth = chnAttr->picWidth;
        chan->attr.pixFmt = chnAttr->pixFmt;
        chan->attr.crop.enable = chnAttr->crop.enable;
        chan->attr.crop.top = chnAttr->crop.top;
        chan->attr.crop.left = chnAttr->crop.left;
        chan->attr.crop.width = chnAttr->crop.width;
        chan->attr.crop.height = chnAttr->crop.height;
        chan->attr.scaler.enable = chnAttr->scaler.enable;
        chan->attr.scaler.outwidth = chnAttr->scaler.outwidth;
        chan->attr.scaler.outheight = chnAttr->scaler.outheight;
        chan->attr.outFrmRateDen = chnAttr->outFrmRateDen;
        chan->attr.outFrmRateNum = chnAttr->outFrmRateNum;
        chan->attr.nrVBs = chnAttr->nrVBs;

        format.pix.colorspace = V4L2_COLORSPACE_SRGB;
        format.rate_bits = 0;
        format.rate_mask = 1;

        if (access("/tmp/fsattr", F_OK) == 0) {
            dump_framesource_chn_attr(chnNum, &chan->attr);
        }

        if ((ret = ioctl(chan->fs_fd, VIDIOC_SET_FRAME_FORMAT, &format)) == 0) {
            IMP_LOG_INFO(TAG, "[chn%d]: width = %d height = %d\n", chan->index, format.pix.width, format.pix.height);
        }else{
            IMP_LOG_ERR(TAG, "[chn%d]: VIDIOC_S_FMT error\n", chan->index);
            return -1;
        }
    } else {
		chan->attr = *chnAttr;
	}

	return 0;
}

int IMP_FrameSource_GetChnAttr(int chnNum, IMPFSChnAttr *chnAttr)
{
	if (chnAttr == NULL) {
		IMP_LOG_ERR(TAG, "%s(): chnAttr is NULL\n", __func__);
		return -1;
	}

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	Framesource *framesource = get_framesource();
	if (framesource == NULL) {
		IMP_LOG_ERR(TAG, "%s(): FrameSource is invalid,"
					"maybe system was not inited yet.\n", __func__);
		return -1;
	}

	FSChannel *channel = &framesource->channel[chnNum];

	if (channel->attr.picWidth == 0) {
		IMP_LOG_ERR(TAG, "%s(): chnAttr was not set yet\n", __func__);
		return -1;
	} else {
		*chnAttr = channel->attr;
	}

	return 0;
}

static void IMP_FrameSource_ReleaseDepthList(struct FSDepth *head, int *cnt)
{
	struct FSDepth *list = NULL;
	if (head == NULL) {
		return;
	}

	while(!is_list_empty(head)) {
		list_del_head(&list, head);
		if (list->frame) {
			if (list->frame->virAddr) {
				free((void *)list->frame->virAddr);
			}
			free(list->frame);
		}
		free(list);
		(*cnt)++;
	}
	free(head);
}

int IMP_FrameSource_SetFrameDepth(int chnNum, int depth)
{
	int i;
	void *virAddr;
	FSChannel *channel;
	IMPFrameInfo *frame;
	Framesource *framesource;

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	if ((depth > NR_MAX_FS_DEPTH) && (depth < 0)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid depth %d\n", __func__, depth);
		return -1;
	}

	framesource = get_framesource();
	channel = &(framesource->channel[chnNum]);
	if ((depth > 0) && (channel->state != FSCHANNEL_STATE_RUN)) {
		IMP_LOG_ERR(TAG, "%s(): Please use IMP_FrameSource_EnableChn first when depth(%d) > 0 \n", __func__, depth);
		return -1;
	}

	pthread_mutex_lock(&(channel->mutex));
	if ((channel->depth == 0) && (depth > 0)) {
		channel->list_prepare = calloc(1, sizeof(struct FSDepth));
		if(channel->list_prepare == NULL) {
			IMP_LOG_ERR(TAG, "%s(): list calloc error !\n", __func__);
			goto err_list_prepare_calloc_null;
		}

		channel->list_done = calloc(1, sizeof(struct FSDepth));
		if(channel->list_done == NULL) {
			IMP_LOG_ERR(TAG, "%s(): list calloc error !\n", __func__);
			goto err_list_done_calloc_null;
		}

		channel->list_used = calloc(1, sizeof(struct FSDepth));
		if(channel->list_used == NULL) {
			IMP_LOG_ERR(TAG, "%s(): list calloc error !\n", __func__);
			goto err_list_used_calloc_null;
		}

		channel->list_done->frame = NULL;
		channel->list_done->next = channel->list_done;
		channel->list_done->prev = channel->list_done;

		channel->list_prepare->frame = NULL;
		channel->list_prepare->next = channel->list_prepare;
		channel->list_prepare->prev = channel->list_prepare;

		channel->list_used->frame = NULL;
		channel->list_used->next = channel->list_used;
		channel->list_used->prev = channel->list_used;

		for(i = 0; i < depth; i++) {
			struct FSDepth *list = NULL;
			list = calloc(1, sizeof(struct FSDepth));
			if(list == NULL) {
				IMP_LOG_ERR(TAG, "%s(): list calloc error !\n", __func__);
				return -1;
			}

			frame = calloc(1, sizeof(IMPFrameInfo));
			if(frame == NULL) {
				IMP_LOG_ERR(TAG, "%s(): calloc depth %d error !\n", __func__, depth);
				return -1;
			}

			frame->size = channel->fr_size;

			virAddr = NULL;
			virAddr = calloc(1, frame->size);
			if(virAddr == NULL) {
				IMP_LOG_ERR(TAG, "calloc frame error\n");
				return -1;
			}

			frame->virAddr = (uint32_t)virAddr;

			list->frame = frame;

			list_add_head(list, channel->list_prepare);
		}
		channel->depth = depth;
	} else if ((channel->depth > 0) && (depth == 0)) {
		int org_depth = channel->depth;
		int freecnt = 0;
		channel->depth = 0;
		IMP_FrameSource_ReleaseDepthList(channel->list_done, &freecnt);
		IMP_FrameSource_ReleaseDepthList(channel->list_used, &freecnt);
		IMP_FrameSource_ReleaseDepthList(channel->list_prepare, &freecnt);
		if (freecnt != org_depth) {
			IMP_LOG_ERR(TAG, "Release %d not equal to logical depth %d node\n", freecnt, org_depth);
			goto err_release_not_equal_logical_exit;
		}
	}
	pthread_mutex_unlock(&(channel->mutex));

	return 0;

err_release_not_equal_logical_exit:
err_list_used_calloc_null:
err_list_done_calloc_null:
err_list_prepare_calloc_null:
	pthread_mutex_unlock(&(channel->mutex));
	return -1;
}

int IMP_FrameSource_GetFrameDepth(int chnNum, int *depth)
{
	FSChannel *channel;
	Framesource *framesource;

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	framesource = get_framesource();
	channel = &framesource->channel[chnNum];

	*depth = channel->depth;

	return 0;

}

int IMP_FrameSource_CreateChn(int chnNum, IMPFSChnAttr *chnAttr)
{
    Framesource *framesource = get_framesource();
    Device *dev = framesource->device;
    FSChannel *chan = NULL;
	int direct_mode = 0;
    int ret = 0;
    int i = 0;
#if 0
    IMPCPUID cpu_id;
    cpu_id = get_cpu_id();
    if ((cpu_id == SOC_T21_L)&&((chnAttr->picWidth > 1280)||(chnAttr->picHeight > 720))) {
        IMP_LOG_ERR(TAG, "Error: width/height is too large!!!! frame->width = %d, frame->height = %d\n", chnAttr->picWidth, chnAttr->picHeight);
        return -1;
    }
#endif
    if ((chnNum > NR_MAX_FS_CHNS - 1) || (chnNum < 0)) {
	IMP_LOG_ERR(TAG, "Invalid channel num%d\n", chnNum);
	return -1;
    }

    if (chnAttr == NULL) {
	IMP_LOG_ERR(TAG, "chnNum=%d, chnAttr is NULL\n", chnNum);
	return -1;
    }

    if ((chnNum == 0) && (chnAttr->type != FS_PHY_CHANNEL)) {
	IMP_LOG_ERR(TAG, "chnNum=%d type=%d must be set to FS_PHY_CHANNEL(%d)\n", chnNum, chnAttr->type, FS_PHY_CHANNEL);
	return -1;
    }

    if (access("/tmp/fsattr", F_OK) == 0) {
	dump_framesource_chn_attr(chnNum, chnAttr);
    }

    if (check_framesource_chn_attr(chnNum, chnAttr) < 0) {
        IMP_LOG_ERR(TAG, "%s:chnNum=%d, check_framesource_chn_attr failed\n", __func__, chnNum);
        return -1;
    }

    chan = &framesource->channel[chnNum];
	chan->direct_mode = 0;
	if ((0 == chnNum) || (3 == chnNum) || (6 == chnNum)) {
		int ivdc_fd = open("/dev/misc-ivdc", O_RDONLY);
		if (ivdc_fd < 0) {
			IMP_LOG_ERR(TAG,"open misc-ivdc failed\n");
			return -1;
		}
		ret = ioctl(ivdc_fd, TISP_VIDIOC_GET_DIRECT, &direct_mode);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "%s(%d): TISP_VIDIOC_GET_DIRECT failed\n", __func__, __LINE__);
			return -1;
		}
		chan->ivdc_fd = ivdc_fd;
		if (direct_mode) {
			chan->direct_mode = direct_mode;
		}
		if ((1 == chan->direct_mode) || (2 == chan->direct_mode) || (3 == chan->direct_mode)) {
			chan->data_threshold = 300;
		}
	}

    if (chan->state < FSCHANNEL_STATE_OPEN){
	pthread_mutex_init(&(chan->mutex), NULL);
	pthread_cond_init(&(chan->cond), NULL);
	pthread_cond_init(&(chan->cond_timedframe), NULL);
	IMP_FrameSource_SetFrameDepth(chnNum, 0);

	ret = framesource_create_group(chnNum);
	if (ret < 0) {
	    IMP_LOG_ERR(TAG, "Failed to create group%d\n", chnNum);
	    return -1;
	}

	chan->index = chnNum;
	chan->group = dev->groups[chnNum];
	chan->state = FSCHANNEL_STATE_OPEN;
	chan->attr = *chnAttr;
	chan->fifo_frame_maxcnt = 0;
	chan->fifo_frame_cnt = 0;
	chan->wait_frame_ts = 0;
	chan->wait_frame_flag = 0;

	/* it's all right */
	framesource->state += FSCHANNEL_STATE_OPEN;
    }

    return ret;
}

int IMP_FrameSource_DestroyChn(int chnNum)
{
	Framesource *framesource = get_framesource();
	FSChannel *chan = NULL;

	if (chnNum > NR_MAX_FS_CHNS - 1) {
		IMP_LOG_ERR(TAG, "Invalid channel num%d\n", chnNum);
		return -1;
	}

	chan = &framesource->channel[chnNum];
	if ((0 == chnNum) || (3 == chnNum) || (6 == chnNum)) {
		close(chan->ivdc_fd);
		chan->ivdc_fd = -1;
	}
	if(chan->state > FSCHANNEL_STATE_OPEN){
		IMP_LOG_ERR(TAG, "channel num%d is busy, please disable it firstly\n", chnNum);
		return -1;
	}else if (chan->state == FSCHANNEL_STATE_OPEN){
		IMP_FrameSource_SetFrameDepth(chnNum, 0);
		pthread_mutex_destroy(&chan->mutex);
		pthread_cond_destroy(&chan->cond);
		pthread_cond_destroy(&chan->cond_timedframe);
		framesource_destroy_group(chnNum);
		chan->group = NULL;
		chan->state = FSCHANNEL_STATE_CLOSE;
		framesource->state -= FSCHANNEL_STATE_OPEN;
	}

	return 0;
}

static int do_reset_channel_attr(FSChannel *channel)
{	IMPFSChnAttr chn_attr = channel->attr;
	struct frame_image_format format;
	int ret = 0;
	//int tmp = -1;

	memset(&format, 0, sizeof(struct frame_image_format));
	if (chn_attr.type == FS_PHY_CHANNEL) {
		if (chn_attr.pixFmt == PIX_FMT_RAW) {
			if(channel->index != 0) {
				IMP_LOG_ERR(TAG, "RAW is only channel == 0 !\n");
				return -1;
			}
			format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
			format.pix.field = V4L2_FIELD_ANY;
			if ((ret = ioctl(channel->fs_fd, VIDIOC_GET_FRAME_FORMAT, &format)) != 0) {
				printf("***** Failed to get format of fr! *****\n");
				return -1;
			}

			chn_attr.picWidth = format.pix.width;
			chn_attr.picHeight = format.pix.height;
			chn_attr.pixFmt = format.pix.pixelformat;
			channel->fr_size = (uint32_t)format.pix.sizeimage;
		} else if ((PIX_FMT_HSV == chn_attr.pixFmt) | (PIX_FMT_RGBA == chn_attr.pixFmt)) {
				IMP_LOG_ERR(TAG, "FS_PHY_CHANNEL do not support HSV == 0 !\n");
				return -1;
		} else {
			channel->fr_size = calc_pic_size(chn_attr.picWidth, chn_attr.picHeight, chn_attr.pixFmt);

			format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
			format.pix.field = V4L2_FIELD_ANY;
			format.pix.width = chn_attr.picWidth;
			format.pix.height = chn_attr.picHeight;
			format.pix.pixelformat = imppixfmt_to_v4l2pixfmt(chn_attr.pixFmt);
			format.crop_enable = chn_attr.crop.enable;
			format.crop_top = chn_attr.crop.top;
			format.crop_left = chn_attr.crop.left;
			format.crop_width = chn_attr.crop.width;
			format.crop_height = chn_attr.crop.height;

			format.scaler_enable = chn_attr.scaler.enable;
			format.scaler_out_width = chn_attr.scaler.outwidth;
			format.scaler_out_height = chn_attr.scaler.outheight;

			format.fcrop_enable = chn_attr.fcrop.enable;
			format.fcrop_top = chn_attr.fcrop.top;
			format.fcrop_left = chn_attr.fcrop.left;
			format.fcrop_width = chn_attr.fcrop.width;
			format.fcrop_height = chn_attr.fcrop.height;
		}

		format.pix.colorspace = V4L2_COLORSPACE_SRGB;
		format.rate_bits = 0;
		format.rate_mask = 1;

		/* set fmt */
		if ((ret = ioctl(channel->fs_fd, VIDIOC_SET_FRAME_FORMAT, &format)) == 0) {
			IMP_LOG_INFO(TAG, "[chn%d]: width = %d height = %d\n", channel->index,
					format.pix.width, format.pix.height);
		}else{
			IMP_LOG_ERR(TAG, "[chn%d]: VIDIOC_S_FMT error\n", channel->index);
			IMP_LOG_ERR(TAG, "[chn%d]: chn_attr.picWidth=%d, chn_attr.picHeight=%d, chn_attr.pixFmt=%d\n",
					channel->index, chn_attr.picWidth, chn_attr.picHeight, chn_attr.pixFmt);
			IMP_LOG_ERR(TAG, "[chn%d]: chn_attr.crop.enable=%d, chn_attr.crop.top=%d, chn_attr.crop.left=%d, chn_attr.crop.width=%d, chn_attr.crop.height=%d\n",
					channel->index, chn_attr.crop.enable, chn_attr.crop.top, chn_attr.crop.left, chn_attr.crop.width, chn_attr.crop.height);
			IMP_LOG_ERR(TAG, "[chn%d]: chn_attr.scaler.enable=%d, chn_attr.scaler.outwidth=%d, chn_attr.scaler.outheight=%d\n",
					channel->index, chn_attr.scaler.enable, chn_attr.scaler.outwidth, chn_attr.scaler.outheight);
			IMP_LOG_ERR(TAG, "[chn%d]: chn_attr.fcrop.enable=%d, chn_attr.fcrop.top=%d, chn_attr.fcrop.left=%d, chn_attr.fcrop.width=%d, chn_attr.fcrop.height=%d\n",
				    channel->index, chn_attr.fcrop.enable, chn_attr.fcrop.top, chn_attr.fcrop.left, chn_attr.fcrop.width, chn_attr.fcrop.height);
			return -1;
		}

	} else {
		format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		format.pix.field = V4L2_FIELD_ANY;
		format.pix.width = chn_attr.picWidth;
		format.pix.height = chn_attr.picHeight;
		if(chn_attr.pixFmt == PIX_FMT_RAW) {
			format.pix.pixelformat = PIX_FMT_RAW;
			channel->fr_size = (uint32_t)(chn_attr.picWidth*chn_attr.picHeight*2);
		} else if (chn_attr.pixFmt == PIX_FMT_HSV) {
			format.pix.pixelformat = PIX_FMT_HSV;
			channel->fr_size = (uint32_t)(chn_attr.picWidth*chn_attr.picHeight*4);
		} else if (chn_attr.pixFmt == PIX_FMT_RGBA) {
			format.pix.pixelformat = PIX_FMT_RGBA;
			channel->fr_size = (uint32_t)(chn_attr.picWidth*chn_attr.picHeight*4);
		}else {
			format.pix.pixelformat = imppixfmt_to_v4l2pixfmt(chn_attr.pixFmt);
			channel->fr_size = calc_pic_size(chn_attr.picWidth, chn_attr.picHeight, chn_attr.pixFmt);
		}
	}

	channel->drv_fmt = format;
	channel->fmt.type = format.type;
	channel->fmt.fmt.pix = format.pix;

	return 0;
}

int IMP_FrameSource_SetMaxDelay(int chnNum, int maxcnt)
{
	FSChannel *channel;
	Framesource *framesource;

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}
	if ((maxcnt < 0)||(maxcnt > NR_MAX_FRAME_DELAY)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid maxcnt %d\n", __func__, maxcnt);
		return -1;
	}
	framesource = get_framesource();
	channel = &(framesource->channel[chnNum]);
	if (channel->state != FSCHANNEL_STATE_OPEN) {
		IMP_LOG_ERR(TAG, "%s(): Please use between IMP_FrameSource_CreateChn and IMP_FrameSource_EnableChn\n", __func__);
		return -1;
	}
	pthread_mutex_lock(&(channel->mutex));
	channel->fifo_frame_maxcnt = maxcnt;
	pthread_mutex_unlock(&(channel->mutex));
	return 0;
}

int IMP_FrameSource_GetMaxDelay(int chnNum, int *maxcnt)
{
	FSChannel *channel;
	Framesource *framesource;

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}
	framesource = get_framesource();
	channel = &(framesource->channel[chnNum]);
	if (channel->state < FSCHANNEL_STATE_OPEN) {
		IMP_LOG_ERR(TAG, "%s(): Please use IMP_FrameSource_CreateChn first\n", __func__);
		return -1;
	}
	pthread_mutex_lock(&(channel->mutex));
	*maxcnt = channel->fifo_frame_maxcnt;
	pthread_mutex_unlock(&(channel->mutex));
	return 0;
}

int IMP_FrameSource_SetDelay(int chnNum, int cnt)
{
	FSChannel *channel;
	Framesource *framesource;

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}
	if ((cnt < 0)||(cnt > NR_MAX_FRAME_DELAY)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid maxcnt %d\n", __func__, cnt);
		return -1;
	}
	framesource = get_framesource();
	channel = &(framesource->channel[chnNum]);
	if (channel->state < FSCHANNEL_STATE_OPEN) {
		IMP_LOG_ERR(TAG, "%s(): Please use IMP_FrameSource_CreateChn first\n", __func__);
		return -1;
	}
	if (cnt > channel->fifo_frame_maxcnt) {
		IMP_LOG_ERR(TAG, "%s(): Invalid cnt %d\n", __func__, cnt);
		return -1;
	}
	pthread_mutex_lock(&(channel->mutex));
	channel->fifo_frame_cnt = cnt;
	pthread_mutex_unlock(&(channel->mutex));
	return 0;
}

int IMP_FrameSource_GetDelay(int chnNum, int *cnt)
{
	FSChannel *channel;
	Framesource *framesource;

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}
	framesource = get_framesource();
	channel = &(framesource->channel[chnNum]);
	if (channel->state < FSCHANNEL_STATE_OPEN) {
		IMP_LOG_ERR(TAG, "%s(): Please use IMP_FrameSource_CreateChn first\n", __func__);
		return -1;
	}
	pthread_mutex_lock(&(channel->mutex));
	*cnt = channel->fifo_frame_cnt;
	pthread_mutex_unlock(&(channel->mutex));
	return 0;
}

int IMP_FrameSource_SetChnFifoAttr(int chnNum, IMPFSChnFifoAttr *attr)
{
	int ret = 0;
	FSChannel *channel;
	Framesource *framesource;
	if(!attr){
		IMP_LOG_ERR(TAG, "%s(): Parameter is wrong\n", __func__);
		return -1;
	}

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	framesource = get_framesource();
	channel = &(framesource->channel[chnNum]);
	if (channel->state != FSCHANNEL_STATE_OPEN) {
		IMP_LOG_ERR(TAG, "%s(): Please use between IMP_FrameSource_CreateChn and IMP_FrameSource_EnableChn\n", __func__);
		return -1;
	}

	ret = IMP_FrameSource_SetMaxDelay(chnNum, attr->maxdepth);
	channel->fifo_type = attr->type;
	channel->fifo_frame_cnt = attr->maxdepth;
	return ret;
}

int IMP_FrameSource_GetChnFifoAttr(int chnNum, IMPFSChnFifoAttr *attr)
{
	FSChannel *channel;
	Framesource *framesource;

	if(!attr){
		IMP_LOG_ERR(TAG, "%s(): Parameter is wrong\n", __func__);
		return -1;
	}
	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}
	framesource = get_framesource();
	channel = &(framesource->channel[chnNum]);
	if (channel->state < FSCHANNEL_STATE_OPEN) {
		IMP_LOG_ERR(TAG, "%s(): Please use IMP_FrameSource_CreateChn first\n", __func__);
		return -1;
	}
	attr->type = channel->fifo_type;
	pthread_mutex_lock(&(channel->mutex));
	attr->maxdepth = channel->fifo_frame_maxcnt;
	pthread_mutex_unlock(&(channel->mutex));
	return 0;
}

int IMP_FrameSource_CloseNCUInfo(void) {
	VBM *vbm = VBMGetInstance();
	if(vbm ==NULL) {
		IMP_LOG_ERR(TAG, "%s(): VBMGetInstance failed\n", __func__);
		return -1;
	}
	vbm->is_close_ncu = 1;
	return 0;
}

int IMP_FrameSource_EnableChn(int chnNum)
{
	int ret = 0;
	int fifo_maxcnt = 0;
	int banks = 0;
	int poolID = -1;
	struct v4l2_requestbuffers req;
	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	Framesource *framesource = get_framesource();
	if (framesource == NULL) {
		IMP_LOG_ERR(TAG, "%s(): FrameSource is invalid,"
					"maybe system was not inited yet.\n", __func__);
		return -1;
	}

	FSChannel *channel = &framesource->channel[chnNum];
	if(channel->state == FSCHANNEL_STATE_RUN){
        IMP_LOG_WARN(TAG, "%s(%d):chn=%d, channel->state is run\n", __func__, __LINE__, chnNum);
		return 0;
	}

	if ((chnNum == 0) && (channel->attr.type != FS_PHY_CHANNEL)) {
		IMP_LOG_ERR(TAG, "chnNum=%d type=%d must be set to FS_PHY_CHANNEL(%d)\n", chnNum, channel->attr.type, FS_PHY_CHANNEL);
		return -1;
	}
	if ((0 == channel->index) && (channel->direct_mode)) {
		int stride = 0;
		int sizeimage = 0;
		int ivdc_mem_line = 0;
		unsigned int ivdc_paddr = 0;

		ret = ioctl(channel->ivdc_fd, TISP_VIDIOC_MEM_LINE, &ivdc_mem_line);
		if(ret < 0) {
			IMP_LOG_ERR(TAG, "[%s][%d] TISP_VIDIOC_MEM_LINE failed\n", __func__, __LINE__);
			return -1;
		}
		stride = (channel->attr.picWidth + 255) & (~255);
		if (0 == ivdc_mem_line) {
			sizeimage = stride * channel->attr.picHeight * 3 / 8;
		} else {
			sizeimage = stride * ivdc_mem_line * 3 / 2;
		}

		channel->ivdc_alloc_addr = malloc(sizeof(IMPAlloc));
		if (!channel->ivdc_alloc_addr) {
			IMP_LOG_ERR(TAG, "[%s][%d] malloc ivdc_alloc_addr failed\n", __func__, __LINE__);
			return -1;
		}

		IMPAlloc *ivdc_alloc = channel->ivdc_alloc_addr;
		poolID = IMP_FrameSource_GetPool(chnNum);
		if (poolID < 0) {
			ret = IMP_Alloc(ivdc_alloc, sizeimage, "ivdc");
		} else {
			ret = IMP_PoolAlloc(poolID, ivdc_alloc, sizeimage, "ivdc");
		}
		if(ret < 0){
			IMP_LOG_ERR(TAG, "[%s][%d] IMP_ALLOC failed size = %d \n",__func__, __LINE__, sizeimage);
			return -1;
		}
		ivdc_paddr = ivdc_alloc->info.paddr;
		ret = ioctl(channel->ivdc_fd, TISP_VIDIOC_SET_PADDR, &ivdc_paddr);
		if(ret < 0) {
			IMP_LOG_ERR(TAG, "[%s][%d] TISP_VIDIOC_SET_PADDR failed\n", __func__, __LINE__);
			return -1;
		}
	}

	if (channel->attr.type == FS_PHY_CHANNEL) {
		char fd_name[32];
		sprintf(fd_name, "/dev/framechan%d", channel->index); //the device node of frame-channels are video1,video2 or video3.
		int fd, openTimes = 0x100;
		while (((fd = open(fd_name, O_RDWR | O_CLOEXEC, 0)) < 0) && (openTimes-- > 0)) {
			usleep(10000);
		}
		if (fd < 0) {
			IMP_LOG_ERR(TAG, "Failed to open channel num%d:%s\n", chnNum, strerror(errno));
			return -1;
		}

		channel->fs_fd = fd;
	}

	ret = do_reset_channel_attr(channel);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "%s(): chn %d reset channel attr error\n", __func__, chnNum);
		goto failed_setattr;
	}

	fifo_maxcnt = channel->fifo_frame_maxcnt;
	if (fifo_maxcnt) {
		channel->fifo_frame = fifo_alloc(fifo_maxcnt+1, sizeof(uint32_t));
		if (NULL == channel->fifo_frame) {
			IMP_LOG_ERR(TAG, "Failed to fifo_alloc chn:%d\n", chnNum);
			return -1;
		}
	}

	if (channel->attr.type == FS_PHY_CHANNEL) {
		/* videobuffer manager */
		VBMPoolConfig poolcfg;
		poolcfg.fmt = channel->fmt;
		poolcfg.nr_buffers = channel->attr.nrVBs;
		if (fifo_maxcnt)
			poolcfg.nr_buffers += fifo_maxcnt;

		if (channel->attr.pixFmt == PIX_FMT_RAW) {
			channel->attr.picWidth = poolcfg.fmt.fmt.pix.width;
			channel->attr.picHeight = poolcfg.fmt.fmt.pix.height;
		} else {
			poolcfg.fmt.fmt.pix.width = channel->attr.picWidth;
			poolcfg.fmt.fmt.pix.height = channel->attr.picHeight;
		}

		VBMInterface vbm_interface;
		vbm_interface.getFrame = get_frame;
		vbm_interface.releaseFrame = release_Frame;

		ret = VBMCreatePool(chnNum, &poolcfg, &vbm_interface, framesource);
		if(ret < 0){
			IMP_LOG_ERR(TAG, "%s(): Failed to VBMCreatePool!\n", __func__);
			return -1;
		}
		/* reqbufs */
		memset(&req, 0, sizeof(struct v4l2_requestbuffers));
		req.count = poolcfg.nr_buffers;
		req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		req.memory = V4L2_MEMORY_USERPTR;

		if (-1 == ioctl (channel->fs_fd, VIDIOC_REQBUFS, &req)) {
			IMP_LOG_ERR(TAG, "[chn%d] does not support user memory\n", chnNum);
			goto failed_reqbufs;
		}

		if (fifo_maxcnt) {
			banks = channel->attr.nrVBs;
			if (-1 == ioctl (channel->fs_fd, VIDIOC_DEFAULT_CMD_SET_BANKS, &banks)) {
				IMP_LOG_ERR(TAG, "[chn%d] does not support set banks\n", chnNum);
				goto failed_set_banks;
			}
		}

		if (req.count < poolcfg.nr_buffers) {
			IMP_LOG_ERR(TAG, "Insufficient buffer memory on chn%d\n", chnNum);
			goto failed_count;
		}

		ret = VBMFillPool(chnNum);
		if(ret < 0){
			IMP_LOG_ERR(TAG, "%s(): Failed to VBMFillPool!\n", __func__);
			goto failed_fillpool;
		}

		channel->pbuf_resize  = 0;

		channel->qframecnt = 0;
		channel->dqframecnt = 0;

		enum v4l2_buf_type type;
		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		if (-1 == (ret = ioctl(channel->fs_fd, VIDIOC_STREAMON, &type))) {
			IMP_LOG_ERR(TAG, "[chn%d] VIDIOC_STREAMON failed\n", chnNum);
			goto failed_streamon;
		}

		/* Init the tick thread. */
		ret = pthread_create(&channel->tid, NULL,
							 frame_pooling_thread, channel);
		if (ret) {
			IMP_LOG_ERR(TAG, "thread create error,ret=%d:%s\n", ret, strerror(ret));
			goto failed_pthread;
		}

		channel->state = FSCHANNEL_STATE_RUN;
		framesource->state += FSCHANNEL_STATE_RUN;
		framesource->FS_EnablePHYChn_Num++;

		return 0;
	failed_streamon:
	failed_pthread:
		VBMFlushFrame(chnNum);
	failed_fillpool:
	failed_count:
	failed_set_banks:
		req.count  = 0;
		ioctl (channel->fs_fd, VIDIOC_REQBUFS, &req);
	failed_reqbufs:
		VBMDestroyPool(chnNum);
	failed_setattr:
		close(channel->fs_fd);
		channel->fs_fd = 0;
		if (channel->ivdc_alloc_addr) {
			IMPAlloc *alloc_ivdc = channel->ivdc_alloc_addr;
			if (alloc_ivdc->info.vaddr) {
				if (poolID < 0) {
					IMP_Free(alloc_ivdc, (void *)(alloc_ivdc->info.vaddr));
				} else {
					IMP_PoolFree(poolID, alloc_ivdc, (void *)(alloc_ivdc->info.vaddr));
				}
			}
			free(alloc_ivdc);
		}
	} else {
		VBMPoolConfig poolcfg;
		poolcfg.fmt = channel->fmt;
		poolcfg.nr_buffers = channel->attr.nrVBs;
		poolcfg.fmt.fmt.pix.width = channel->attr.picWidth;
		poolcfg.fmt.fmt.pix.height = channel->attr.picHeight;
		if (fifo_maxcnt)
			poolcfg.nr_buffers += fifo_maxcnt;

		VBMInterface vbm_interface;
		vbm_interface.getFrame = ext_channel_get_frame;
		vbm_interface.releaseFrame = ext_channel_release_frame;

		ret = VBMCreatePool(chnNum, &poolcfg, &vbm_interface, framesource);
		if(ret < 0){
			IMP_LOG_ERR(TAG, "%s(): Failed to VBMCreatePool!\n", __func__);
			return -1;
		}

		int size_of_ext_buffer = sizeof(struct ExtChannelBuffer) + channel->attr.nrVBs * sizeof(IMPFrameInfo *);
		channel->ext_buffer = malloc(size_of_ext_buffer);
		if (!channel->ext_buffer) {
			IMP_LOG_ERR(TAG, "%s(): malloc ext_buffer failed\n", __func__);
			goto failed_ext_buffer;
		}

		memset(channel->ext_buffer, 0, size_of_ext_buffer);
		channel->ext_buffer->nr_buffers = channel->attr.nrVBs;
		channel->qframecnt = 0;
		channel->dqframecnt = 0;

		int max_w = 0, max_h = 0;
		if (IS_SOC_SERIES(T10)) {
			max_w = 1280;
			max_h = 960;
		} else if (IS_SOC_SERIES(T20)) {
			max_w = 1920;
			max_h = 1080;
		} else if (IS_SOC_SERIES(T30)) {
			max_w = 2592;
			max_h = 2048;
		} else if (IS_SOC_SERIES(T21)) {
			max_w = 1920;
			max_h = 1080;
		}

		channel->ext_buffer->resize_tmp_buf = malloc(SIZE_OF_RESIZE_TMP_BUF(max_w, max_h));
		if (!channel->ext_buffer->resize_tmp_buf) {
			IMP_LOG_ERR(TAG, "%s(): malloc ext_buffer failed\n", __func__);
			goto failed_resize_tmp_buf;
		}
		if ((PIX_FMT_HSV == channel->attr.pixFmt) | (PIX_FMT_RGBA == channel->attr.pixFmt)) {
			uint32_t size = sizeof(IMPAlloc);
			channel->ext_buffer->resize_frame_buf = malloc(size);
			if (!channel->ext_buffer->resize_frame_buf) {
				IMP_LOG_ERR(TAG, "%s(): malloc ext resize frame buffer failed\n", __func__);
				goto failed_resize_frame_buf;
			}
			memset(channel->ext_buffer->resize_frame_buf, 0, size);
		}
		sem_init(&channel->ext_sync_sem, 0, 1);

		channel->state = FSCHANNEL_STATE_RUN;
		framesource->state += FSCHANNEL_STATE_RUN;

		return 0;
	failed_resize_frame_buf:
		free(channel->ext_buffer->resize_tmp_buf);
	failed_resize_tmp_buf:
		free(channel->ext_buffer);
	failed_ext_buffer:
		VBMDestroyPool(chnNum);
	}

	return -1;
}

int IMP_FrameSource_DisableChn(int chnNum)
{
	int ret = 0, flush_group_tree_cnt = 10;
	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	Framesource *framesource = get_framesource();
	if (framesource == NULL) {
		IMP_LOG_ERR(TAG, "%s(): FrameSource is invalid,"
					"maybe system was not inited yet.\n", __func__);
		return -1;
	}

	FSChannel *channel = &framesource->channel[chnNum];

	if(channel->state != FSCHANNEL_STATE_RUN){
		IMP_LOG_ERR(TAG, "%s(): chan%d isn't running\n", __func__, chnNum);
		return -1;
	}

	if (channel->attr.type == FS_PHY_CHANNEL) {
		pthread_cancel(channel->tid);
		pthread_join(channel->tid, NULL);
	}

	if (channel->attr.type == FS_EXT_CHANNEL) {
		sem_wait(&channel->ext_sync_sem);
	}
	channel->state = FSCHANNEL_STATE_OPEN;
	if (channel->attr.type == FS_EXT_CHANNEL) {
		sem_post(&channel->ext_sync_sem);
	}

flush_group_tree_again:
	ret = flush_group_tree(channel->group);
	if ((ret < 0) && (flush_group_tree_cnt-- > 0)) {
		goto flush_group_tree_again;
	}
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "flush_group_tree err:%s(%d),chnNum=%d, channel->attr.type=%d\n", __func__, __LINE__, chnNum, channel->attr.type);
	}

	VBMFlushFrame(chnNum);

	if (channel->attr.type == FS_PHY_CHANNEL) {
		enum v4l2_buf_type type;
		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		if (-1 == (ret = ioctl(channel->fs_fd, VIDIOC_STREAMOFF, &type))) {
			IMP_LOG_ERR(TAG, "[chn%d] VIDIOC_STREAMOFF failed\n", chnNum);
			return ret;
		}

		struct v4l2_requestbuffers req;
		memset(&req, 0, sizeof(struct v4l2_requestbuffers));
		req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		req.memory = V4L2_MEMORY_USERPTR;

		if (-1 == (ret = ioctl (channel->fs_fd, VIDIOC_REQBUFS, &req))) {
			IMP_LOG_ERR(TAG, "[chn%d] clean buffer error\n", chnNum);
			return ret;
		}
		framesource->FS_EnablePHYChn_Num--;
		close(channel->fs_fd);
	}

	VBMDestroyPool(chnNum);

	if (channel->attr.type == FS_EXT_CHANNEL) {
		if (channel->ext_buffer->resize_tmp_buf) {
			free(channel->ext_buffer->resize_tmp_buf);
			channel->ext_buffer->resize_tmp_buf = NULL;
		}
		IMPAlloc *alloc = channel->ext_buffer->resize_frame_buf;
		if (alloc) {
			if (alloc->info.vaddr) {
				IMP_Free(alloc, (void*)alloc->info.vaddr);
			}
			free(alloc);
		}
		if (channel->ext_buffer) {
			free(channel->ext_buffer);
			channel->ext_buffer = NULL;
		}
		sem_destroy(&channel->ext_sync_sem);
	}

	if ((0 == channel->index) && (channel->direct_mode)) {
		int poolID = IMP_FrameSource_GetPool(chnNum);
		IMPAlloc *ivdc_alloc = (IMPAlloc *)(channel->ivdc_alloc_addr);
		if (ivdc_alloc) {
			if (ivdc_alloc->info.vaddr) {
				if (poolID < 0) {
					IMP_Free(ivdc_alloc, (void *)(ivdc_alloc->info.vaddr));
				} else {
					IMP_PoolFree(poolID, ivdc_alloc, (void *)(ivdc_alloc->info.vaddr));
				}
			}
			free(ivdc_alloc);
		}
	}

	framesource->state -= FSCHANNEL_STATE_RUN;
	if (channel->fifo_frame)
		fifo_free(channel->fifo_frame);
	if (channel->pbuf_resize) {
		free(channel->pbuf_resize);
		channel->pbuf_resize = 0;
	}

	return 0;
}

int IMP_FrameSource_GetFrame(int chnNum, IMPFrameInfo **frame)
{
	FSChannel *channel;
	struct timeval timeval;
	struct FSDepth *fsdepth;
	struct timespec timespec;
	Framesource *framesource;

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	framesource = get_framesource();
	channel = &(framesource->channel[chnNum]);

	pthread_mutex_lock(&(channel->mutex));
	if (channel->depth > 0) {
		gettimeofday(&timeval, NULL);
		timespec.tv_sec = timeval.tv_sec + 2;
		timespec.tv_nsec = timeval.tv_usec * 1000;

		if(channel->list_done->prev == channel->list_done)
			pthread_cond_timedwait(&(channel->cond), &(channel->mutex), &timespec);
		if(channel->list_done->prev == channel->list_done) {
			IMP_LOG_ERR(TAG, "%s(): cannot get frame !\n", __func__);
			pthread_mutex_unlock(&(channel->mutex));
			return -1;
		}

		fsdepth = channel->list_done->next;
		list_del_FSDepth(fsdepth);
		*frame = fsdepth->frame;
		list_add_head(fsdepth, channel->list_used);
	}
	pthread_mutex_unlock(&(channel->mutex));

	return 0;
}

int IMP_FrameSource_GetTimedFrame(int chnNum, IMPFrameTimestamp *framets, int block, void *framedata, IMPFrameInfo *frame)
{
	int ret = -1;
	FSChannel *channel;
	Framesource *framesource;
	struct timeval timeval;
	struct timespec timespec;
	void *fifo = NULL;

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}
	framesource = get_framesource();
	channel = &(framesource->channel[chnNum]);

	pthread_mutex_lock(&(channel->mutex));
	if (0 == channel->fifo_frame_maxcnt) {
		IMP_LOG_ERR(TAG, "%d(): should set fifo_frame_maxcnt first\n", __LINE__);
		goto err;
	}
	fifo = channel->fifo_frame;
	if (NULL == fifo) {
		IMP_LOG_ERR(TAG, "%d(): fifo frame is null\n", __LINE__);
		goto err;
	}

	IMPFrameInfo *f;
	void *node;
	uint32_t *data;
	uint64_t ts;
	uint64_t fts;
	uint64_t lastfts;
	void *lastnode;
	uint32_t *lastdata;

wait_retry:
	node = NULL;
	data = NULL;
	ts = framets->ts;
	fts = 0;
	lastfts = 0;
	lastnode = NULL;
	lastdata = NULL;
	fifo_head(fifo, &node, (void**)&data);
	while(node) {
		fts = ((IMPFrameInfo*)(*data))->timeStamp;
		/*printf("fts = %lld\n",fts);*/
		if (ts <= fts) {
			if (NULL == lastnode) {
				IMP_LOG_ERR(TAG, "%d(): ts too small ts: %lld, fts: %lld\n", __LINE__, ts, fts);
				ret = -1;
				goto err;
			} else {
				if ((ts-lastfts)<(fts-ts)) {
					f = (IMPFrameInfo*)(*lastdata);
				} else {
					f = (IMPFrameInfo*)(*data);
				}
				ret = 0;
				goto done;
			}
		} else {
			lastfts = fts;
			lastnode = node;
			lastdata = data;
		}
		fifo_node_next(fifo, &node, (void**)&data);
	}
	IMP_LOG_ERR(TAG, "%d(): ts too large %lld\n", __LINE__, ts);
	if (block) {
		channel->wait_frame_flag = 1;
		channel->wait_frame_ts = ts;
		gettimeofday(&timeval, NULL);
		timespec.tv_sec = timeval.tv_sec + 5;
		timespec.tv_nsec = timeval.tv_usec * 1000;
		ret = pthread_cond_timedwait(&(channel->cond_timedframe), &(channel->mutex), &timespec);
		channel->wait_frame_flag = 0;
		if (0 == ret) {
			goto wait_retry;
		} else if (ETIMEDOUT == ret) {
			IMP_LOG_ERR(TAG, "%d(): wait timed frame timeout %lld\n", __LINE__, ts);
			ret = -1;
			goto err;
		} else {
			IMP_LOG_ERR(TAG, "%d(): pthread_cond_timedwait err  %lld\n", __LINE__, ts);
			ret = -1;
			goto err;
		}
	} else {
		ret = -2;
		goto err;
	}
done:
	if (framedata) {
		if (f->pixfmt == V4L2_PIX_FMT_NV12 || f->pixfmt == V4L2_PIX_FMT_NV21) {
			memcpy((void *)framedata, (void*)f->virAddr, f->width * f->height);
			memcpy((void *)framedata + f->width * f->height,
					(void *)f->virAddr + f->width * ((f->height + 15) & ~15),
					f->width * f->height / 2);
		} else {
			memcpy((void *)framedata, (void *)f->virAddr, f->size);
		}
	}
	if (f->pixfmt == V4L2_PIX_FMT_NV12 || f->pixfmt == V4L2_PIX_FMT_NV21) {
		f->size = f->width * f->height * 3 / 2;
	}
	if (frame)
		memcpy(frame, f, sizeof(IMPFrameInfo));
	pthread_mutex_unlock(&(channel->mutex));
	return ret;
err:
	pthread_mutex_unlock(&(channel->mutex));
	return ret;

}

int IMP_FrameSource_ReleaseFrame(int chnNum, IMPFrameInfo *frame)
{
	FSChannel *channel;
	struct FSDepth *fsdepth;
	Framesource *framesource;

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	framesource = get_framesource();
	channel = &(framesource->channel[chnNum]);

	pthread_mutex_lock(&(channel->mutex));
	if (channel->depth > 0) {
		for(fsdepth = channel->list_used->prev; fsdepth != channel->list_used;
				fsdepth = fsdepth->prev) {
			if(fsdepth->frame == frame) {
				list_del_FSDepth(fsdepth);
				list_add_head(fsdepth, channel->list_prepare);
				break;
			}
		}
	}
	pthread_mutex_unlock(&(channel->mutex));

	return 0;
}

int IMP_FrameSource_SnapFrame(int chnNum, IMPPixelFormat fmt, int width, int height, void *framedata, IMPFrameInfo *frame)
{
	int ret = -1;
	FSChannel *channel;
	Framesource *framesource;
	struct timeval timeval;
	struct timespec timespec;

	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(%d): Invalid chnNum %d\n", __func__, __LINE__,  chnNum);
		return -1;
	}
	if (NULL == framedata) {
		IMP_LOG_ERR(TAG, "%s(%d): param framedata %p\n", __func__, __LINE__,  chnNum, framedata);
		return -1;
	}
	framesource = get_framesource();
	channel = &(framesource->channel[chnNum]);

	if (FSCHANNEL_STATE_RUN != channel->state) {
		IMP_LOG_ERR(TAG, "%s(%d): channel %d should run first\n", __func__, __LINE__,  chnNum);
		return -1;
	}
	if ((channel->attr.picWidth != width)&&(channel->attr.picHeight != height)) {
		IMP_LOG_ERR(TAG, "%s(%d): channel %d resolution %dx%d not equal to %dx%d\n",
				__func__, __LINE__,  chnNum, channel->attr.picWidth, channel->attr.picHeight, width, height);
		return -1;
	}

	if (channel->attr.pixFmt != fmt) {
		if (!((PIX_FMT_NV12 == channel->attr.pixFmt)&&(PIX_FMT_YUYV422 == fmt))) {
			IMP_LOG_ERR(TAG, "%s(%d): channel %d pixFmt:%d not equal to param fmt:%d\n", __func__, __LINE__, chnNum, channel->attr.pixFmt, fmt);
			return -1;
		}
	}

	pthread_mutex_lock(&(channel->mutex));
	channel->status_snapframe = STATUS_SNAPFRAME_START;
	channel->fmt_snapframe = fmt;
	channel->pbuf_snapframe = framedata;
	channel->info_snapframe = frame;
	while (STATUS_SNAPFRAME_DONE != channel->status_snapframe) {
		gettimeofday(&timeval, NULL);
		timespec.tv_sec = timeval.tv_sec + 3;
		timespec.tv_nsec = timeval.tv_usec * 1000;
		ret = pthread_cond_timedwait(&channel->cond_snapframe, &channel->mutex, &timespec);
		if (0 == ret) {
		} else if (ETIMEDOUT == ret) {
			IMP_LOG_ERR(TAG, "%s(%d): wait timed frame timeout %d\n", __func__, __LINE__, ret);
			ret = -1;
			goto err;
		} else {
			IMP_LOG_ERR(TAG, "%s(%d): pthread_cond_timedwait err  %d\n", __func__, __LINE__, ret);
			ret = -1;
			goto err;
		}
	}
	pthread_mutex_unlock(&(channel->mutex));
	frame->index = chnNum / 3;
	return ret;
err:
	pthread_mutex_unlock(&(channel->mutex));
	return ret;
}

/* This is a hidden API for 3D-VR Camera */
int IMP_FrameSource_SetFrameOffset(int chnNum, int x, int y)
{
	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	Framesource *framesource = get_framesource();
	FSChannel *channel = &framesource->channel[chnNum];

	channel->x_offset = x;
	channel->y_offset = y;

	return 0;
}


int IMP_FrameSource_EnableChnUndistort(int chnNum, void *handle)
{
	int ret = 0;
	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	Framesource *framesource = get_framesource();
	FSChannel *chan = &framesource->channel[chnNum];

	pthread_mutex_lock(&chan->mutex);
	if ((960 == chan->attr.picWidth)&&(960 == chan->attr.picHeight)) {
		chan->distortion.handle = handle;
		chan->distortion.mode = ((undistort_handle*)handle)->mode;
		chan->distortion.en = 1;
	} else {
		IMP_LOG_WARN(TAG, "%s(): undistort now only support 960x960 resolution\n", __func__);
		chan->distortion.handle = NULL;
		chan->distortion.en = 0;
		ret = -1;
	}
	pthread_mutex_unlock(&chan->mutex);

	return ret;
}

int IMP_FrameSource_DisableChnUndistort(int chnNum)
{
	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	Framesource *framesource = get_framesource();
	FSChannel *chan = &framesource->channel[chnNum];
	pthread_mutex_lock(&chan->mutex);
	if (1 == chan->distortion.en) {
		chan->distortion.en = 0;
	}
	pthread_mutex_unlock(&chan->mutex);
	return 0;
}

/* for debug */

#define TREE_CHILD_MAX_NUM 5
typedef struct {
	Module *pm;
	int x;
	int y;
	int len;
	int child;
	int brother_index;
	int brother_max;
} pos;
pos *pos_info = NULL;
static int point_x = 0;
static int point_y = 0;
static int point_cnt = 0;
int tree_travel_prepare(void)
{
	point_x = 0;
	point_y = 0;
	point_cnt = 0;
	return 0;
}
int tree_travel(Module *pmodule, int brother_index, int brother_max)
{
	int i = 0;
	int j = 0;
	int childnum = 0;
	Module *pm = pmodule;
	if (NULL == pmodule) {
		return 0;
	}
	/* do */
	//printf("name = %s	x = %d\n", pm->p->name, point_x);
	pos_info[point_cnt].x = point_x;
	pos_info[point_cnt].y = point_y;
	pos_info[point_cnt].pm = pmodule;
	pos_info[point_cnt].brother_index = brother_index;
	pos_info[point_cnt].brother_max = brother_max;

	for (i = 0; i < TREE_CHILD_MAX_NUM; i++) {
		if (NULL != pm->observerList[i].module) {
			childnum++;
		}
	}

	if (0 == childnum) {
		point_x++;
		pos_info[point_cnt].child = 0;
	} else
		pos_info[point_cnt].child = 1;

	point_cnt++;

	point_y++;
	j = 0;
	for (i = 0; i < TREE_CHILD_MAX_NUM; i++) {
		if (NULL != pm->observerList[i].module) {
			j++;
			tree_travel(pm->observerList[i].module, j, childnum);
		}
	}
	point_y--;
	return 0;
}

int tree_item_cnt_loop(Module *pmodule)
{
	int i = 0;
	int cnt= 0;
	Module *pm = pmodule;
	if (NULL == pmodule) {
		return 0;
	}

	for (i = 0; i < TREE_CHILD_MAX_NUM; i++) {
		if (NULL != pm->observerList[i].module) {
			cnt++;
			cnt += tree_item_cnt_loop(pm->observerList[i].module);
		}
	}
	return cnt;
}
int tree_item_cnt(Module *pmodule)
{
	int cnt = tree_item_cnt_loop(pmodule);
	return (cnt+1);
}

pos *find_xy(pos *pi, int cnt, int x, int y)
{
	int i = 0;
	for (i = 0; i < cnt; i++) {
		if ((pi[i].x == x) && (pi[i].y == y)) {
			return &pi[i];
		}
	}
	return NULL;
}

int get_xy_max(pos *pi, int cnt, int *xmax, int *ymax)
{
	int i = 0;
	int x_max = 0;
	int y_max = 0;
	for (i = 0; i < cnt; i++) {
		if (pi[i].x > x_max) {
			x_max = pi[i].x;
		}
		if (pi[i].y > y_max) {
			y_max = pi[i].y;
		}
	}
	*xmax = x_max;
	*ymax = y_max;
	return 0;
}

int vertical_line(pos *pi, int cnt, int x, int y)
{
	int i = 0;
	int x1 = -1;
	pos *pi_x1 = NULL;
	for (i = 0; i < cnt; i++) {
		if (pi[i].y == y) {
			if ((pi[i].x < x) && (pi[i].x > x1)) {
				x1 = pi[i].x;
				pi_x1 = &pi[i];
			}
		}
	}
	if (-1 == x1)
		return 0;
	if (pi_x1->brother_max == pi_x1->brother_index)
		return 0;
	return 1;
}

extern void IMP_Alloc_Dump();
int dbg_misc_system_info(void *buf, int msize, void **arg)
{

	int i;
	dformat *df = buf;
	int len = 0;
	int tlen = 0;
	char *dbuf = NULL;


	IMP_Alloc_Dump();
	if (NULL == gFramesource) {
		df->out.ret = -1;
		return -1;
	}
	dbuf = (char *)df->out.udf;

	for (i = 0; i < NR_MAX_FS_CHNS; i++) {
		FSChannel *ch = &gFramesource->channel[i];
		if (NULL == ch->group) continue;
		Module *mod = ch->group->module;
		{

			int loop = 0;
			int x_max = 0;
			int y_max = 0;
			int item_cnt = 0;
			int sem_msg_cnt = 0;
			int sem_cnt = 0;
			item_cnt = tree_item_cnt(mod);
			pos_info = malloc(sizeof(*pos_info)*item_cnt);
			len = sprintf(dbuf, "tree item: %d\n", item_cnt);
			dbuf += len;
			tlen += len;
			memset(pos_info, 0, sizeof(*pos_info)*item_cnt);

			tree_travel_prepare();
			tree_travel(mod, 1, 1);
			get_xy_max(pos_info, item_cnt, &x_max, &y_max);
#if 0
			for (loop = 0; loop < item_cnt; loop++) {
				printf("%s			%d, %d, %d,%d,%d\n",
						pos_info[loop].pm->name, pos_info[loop].x, pos_info[loop].y, pos_info[loop].child, pos_info[loop].brother_index, pos_info[loop].brother_max);
			}
#endif
#if 1
			for (loop = 0; loop < item_cnt; loop++) {
				sem_getvalue(&pos_info[loop].pm->sem_msg, &sem_msg_cnt);
				sem_getvalue(&pos_info[loop].pm->sem, &sem_cnt);
				if (strncmp(pos_info[loop].pm->name, "Framesource", strlen("Framesource")) == 0) {
					len = sprintf(dbuf, "%-20s update_cnt=%d(qframecnt=%d, dqframecnt=%d, sem_msg_cnt=%d, sem_cnt=%d)\n",
							pos_info[loop].pm->name, pos_info[loop].pm->update_cnt,
							ch->qframecnt, ch->dqframecnt, sem_msg_cnt, sem_cnt);
				} else {
					len = sprintf(dbuf, "%-20s update_cnt=%d(sem_msg_cnt=%d, sem_cnt=%d)\n",
							pos_info[loop].pm->name, pos_info[loop].pm->update_cnt, sem_msg_cnt, sem_cnt);
				}
				dbuf += len;
				tlen += len;
			}
#endif
#define TREE_POINT_NAME_SIZE 15
			{
				int i,j;
				char point_str[TREE_POINT_NAME_SIZE + 1];
				for (i = 0; i <= x_max; i++) {
					for (j = 0; j <= y_max; j++) {
						pos *pi = find_xy(pos_info, item_cnt, i, j);
						if (NULL != pi) {
							int sl = strlen(pi->pm->name);
							if ( sl <= TREE_POINT_NAME_SIZE) {
								sprintf(point_str, "%s", pi->pm->name);
								if (pi->child) {
									memset((char *)point_str+sl, '-', TREE_POINT_NAME_SIZE - sl);
									point_str[TREE_POINT_NAME_SIZE] = 0;
								}
							} else {
								strncpy(point_str, pi->pm->name, sl);
							}
							if (pi->brother_index == 1)
								len = sprintf(dbuf, "%s", "---");
							else
								len = sprintf(dbuf, "%s", "|--");
							dbuf += len;
							tlen += len;

							len = sprintf(dbuf, "%s", point_str);
							dbuf += len;
							tlen += len;
						} else {
							memset((char *)point_str, ' ', TREE_POINT_NAME_SIZE);
							point_str[TREE_POINT_NAME_SIZE] = 0;
							if (vertical_line(pos_info, item_cnt, i, j)) {
								len = sprintf(dbuf, "%s", "|  ");
								dbuf += len;
								tlen += len;
								len = sprintf(dbuf, "%s", point_str);
								dbuf += len;
								tlen += len;
							} else {
								len = sprintf(dbuf, "%s", point_str);
								dbuf += len;
								tlen += len;
								len = sprintf(dbuf, "%s", "   ");
								dbuf += len;
								tlen += len;
							}
						}
					}
					len = sprintf(dbuf, "\n");
					dbuf += len;
					tlen += len;
				}
			}
			free(pos_info);
		}

#if 0
		len = sprintf(dbuf, "CHANNEL(%d)\n\t  INFO\t%5dx%5d\t%8s \t%2d/%2d(fps) \t%8s \n\t  CROP\t%8s\tleft(%d)\ttop(%d)\twidth(%d)\theight(%d)\n\tSCALER \t%8s \twidth(%d) \theight(%d)\n",
				i, ch->attr.picWidth, ch->attr.picHeight, fs_stat_str[ch->state], gFramesource->inFrmRateNum, gFramesource->inFrmRateDen,
				ch->attr.pixFmt==PIX_FMT_NV12?"NV12":"NOT-NV12",
				ch->attr.crop.enable?"EN":"DIS", ch->attr.crop.left, ch->attr.crop.top, ch->attr.crop.width, ch->attr.crop.height,
				ch->attr.scaler.enable?"EN":"DIS", ch->attr.scaler.outwidth, ch->attr.scaler.outheight);
		dbuf += len;
		tlen += len;
#endif
	}

	tlen++;
	df->flg |= FUNC_FLAG_END;
	df->out.ret = 0;
	df->out.type = FUNC_OUT_DATA_TYPE_STR;
	df->out.size = tlen;
	return 0;
}

char * fs_stat_str[] = {
	"CLOSE",
	"OPEN",
	"RUN"
};

int dbg_fs_info(void *buf, int msize, void **arg)
{
	int i;
	dformat *df = buf;
	int len = 0;
	int tlen = 0;
	char *dbuf = NULL;

	if (NULL == gFramesource) {
		df->out.ret = -1;
		return -1;
	}
	dbuf = (char *)df->out.udf;

	for (i = 0; i < NR_MAX_FS_CHNS; i++) {
		FSChannel *ch = &gFramesource->channel[i];
		if (NULL == ch->group) continue;
		len = sprintf(dbuf, "CHANNEL(%d)\n\t  INFO\t%5dx%5d\t%8s \t%2d/%2d(fps) \t%8s \n\t  FCROP\t%8s\tleft(%d)\ttop(%d)\twidth(%d)\theight(%d)\n\tCROP\t%8s\tleft(%d)\ttop(%d)\twidth(%d)\theight(%d)\n\tSCALER \t%8s \twidth(%d) \theight(%d)\n",
			      i, ch->attr.picWidth, ch->attr.picHeight, fs_stat_str[ch->state], gFramesource->inFrmRateNum, gFramesource->inFrmRateDen,
			      ch->attr.pixFmt==PIX_FMT_NV12?"NV12":"NOT-NV12",
			      ch->attr.fcrop.enable?"EN":"DIS", ch->attr.fcrop.left, ch->attr.fcrop.top, ch->attr.fcrop.width, ch->attr.fcrop.height,
			      ch->attr.crop.enable?"EN":"DIS", ch->attr.crop.left, ch->attr.crop.top, ch->attr.crop.width, ch->attr.crop.height,
			      ch->attr.scaler.enable?"EN":"DIS", ch->attr.scaler.outwidth, ch->attr.scaler.outheight);
		dbuf += len;
		tlen += len;
	}

	tlen++;
	df->flg |= FUNC_FLAG_END;
	df->out.ret = 0;
	df->out.type = FUNC_OUT_DATA_TYPE_STR;
	df->out.size = tlen;
	return 0;
}

typedef struct dbg_fs_ivdc_s_data {
	int chn;
	int data_threshold;
} dbg_fs_ivdc_s_data_t;

int dbg_fs_ivdc_s(void *buf, int msize, void **arg)
{
	int ret = -1;
	dformat *df = buf;
	int tlen = 0;
	char *dbuf = NULL;
	int chn = 0;
	int data_threshold = 0;
	dbg_fs_ivdc_s_data_t *pparam;

	if (NULL == gFramesource) {
		df->out.ret = -1;
		return -1;
	}
	dbuf = (char *)df->in.udf;
	pparam = (dbg_fs_ivdc_s_data_t *)dbuf;
	chn = pparam->chn;
	data_threshold = pparam->data_threshold;

	if ((0 == chn) || (3 == chn) || (6 == chn)) {
		ret = IMP_FrameSource_SetDirectModeAttr(chn, data_threshold);
		if (0 != ret) {
			df->out.ret = -1;
			return -1;
		}
	}

	tlen++;
	df->flg |= FUNC_FLAG_END;
	df->out.ret = 0;
	df->out.type = FUNC_OUT_DATA_TYPE_STR;
	df->out.size = tlen;
	return 0;
}

typedef struct {
	uint32_t cmd;
	uint32_t param1;
	uint32_t param2;
	uint32_t param3;
} misc_simple_cmd;

//define simple cmd types
#define SCMD_FS_SNAPS (100)

#define SCMD_ISP_RUNNING_MODE (201)
#define SCMD_ISP_SET_FPS (202)

#define SCMD_DUMP (10000)
#define SCMD_DUMP_FS_FRAME_INFO (100)
#define SCMD_DUMP_IVS_BASE_MOVE (800)
extern uint32_t dump_base_move_ivs;
extern void VBMDumpPoolInfo(void);

int dbg_misc_simple_cmd(void *buf, int msize, void **arg)
{
	dformat *df = buf;
	misc_simple_cmd *cmd;

	if (NULL == gFramesource) {
		df->out.ret = -1;
		return -1;
	}
	cmd = (misc_simple_cmd *)df->in.udf;
	printf("scmd = %d param1 = %d, param2 = %d, param3 = %d\n",
			cmd->cmd, cmd->param1, cmd->param2, cmd->param3);
	if (SCMD_FS_SNAPS == cmd->cmd) {
#if 1
		if (cmd->param1 >= 3) {
			printf("err: param1 = %d\n", cmd->param1);
			df->out.ret = -1;
			return 0;
		}
		g_dbg_fs_snap_yuv.en[cmd->param1] = 1;
		g_dbg_fs_snap_yuv.cnt[cmd->param1] = cmd->param2;
#endif
	} else if (SCMD_DUMP == cmd->cmd) {
		if (SCMD_DUMP_FS_FRAME_INFO == cmd->param1) {
			VBMDumpPoolInfo();
		}
		if (SCMD_DUMP_IVS_BASE_MOVE == cmd->param1) {
#if 1
			dump_base_move_ivs = cmd->param2;
#endif
		}
    } else if (SCMD_ISP_RUNNING_MODE == cmd->cmd) {
        int mode = cmd->param1;
        IMP_ISP_Tuning_SetISPRunningMode(mode);
    } else if (SCMD_ISP_SET_FPS == cmd->cmd) {
        int num = cmd->param1;
        int den = cmd->param2;
        IMP_ISP_Tuning_SetSensorFPS(num, den);
    }
	df->out.ret = 0;
	return 0;
}

enum {
	IMP_DBG_FMT_RAW,
	IMP_DBG_FMT_NV12,
	IMP_DBG_FMT_UYVY422,
	IMP_DBG_FMT_YUYV422,
	IMP_DBG_FMT_RGB565BE,
	IMP_DBG_FMT_BGR0,
	IMP_DBG_FMT_BGR24,
};

typedef struct {
	int pic_type;
	char path[0];
} save_pic_udf;

int snap_pic(char *path, int format);
int dbg_misc_save_pic(umem_info *uinfo, void **pri)
{
	int ret = -1;
	IMPPixelFormat format;
	dformat *df = uinfo->shm_buf;
	save_pic_udf *pudf = (save_pic_udf *)df->in.udf;
	if (50 < strlen((char *)pudf->path)) {
		printf("err: file name too long\n");
		return -1;
	}
	printf("info: save pic file name : %s type : %d\n", pudf->path, pudf->pic_type);
	switch(pudf->pic_type) {
		case IMP_DBG_FMT_RAW:
			format = PIX_FMT_RAW;
			break;
		case IMP_DBG_FMT_NV12:
			format = PIX_FMT_NV12;
			break;
		case IMP_DBG_FMT_UYVY422:
			format = PIX_FMT_UYVY422;
			break;
		case IMP_DBG_FMT_YUYV422:
			format = PIX_FMT_YUYV422;
			break;
		case IMP_DBG_FMT_RGB565BE:
			format = PIX_FMT_RGB565BE;
			break;
		case IMP_DBG_FMT_BGR0:
			format = PIX_FMT_BGR0;
			break;
		case IMP_DBG_FMT_BGR24:
			format = PIX_FMT_BGR24;
			break;
		default:
			printf("err: pic format error\n");
			return -1;
			break;
	}
	ret = snap_pic(pudf->path, format);
	return ret;
}


int tree_funcs_control(Module *pmodule, int en)
{
	int i = 0;
	int index = 0;
	int ret = -1;
	Module *pm = pmodule;
	if (NULL == pmodule) {
		return 0;
	}

	for (i = 0; i < TREE_CHILD_MAX_NUM; i++) {
		if (NULL != pm->observerList[i].module) {
			ret = tree_funcs_control(pm->observerList[i].module, en);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):tree_funcs_control failed\n", __func__, __LINE__);
				return -1;
			}
		}
	}
	Group *pg = module_pri(pmodule);
	index = pg->group_index;
	if (!strncmp(pm->name, "OSD", strlen("OSD"))) {
		IMP_LOG_ERR(TAG, "%s: %s\n", pm->name, en?"ENABLE":"DISABLE");
		if (en) {
			ret = IMP_OSD_Start(index);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):IMP_OSD_Start failed\n", __func__, __LINE__);
				return -1;
			}
		} else {
			ret = IMP_OSD_Stop(index);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):IMP_OSD_Stop failed\n", __func__, __LINE__);
				return -1;
			}
		}
	} else if (!strncmp(pm->name, "Encoder", strlen("Encoder"))) {
		IMP_LOG_ERR(TAG, "%s: %s\n", pm->name, en?"ENABLE":"DISABLE");
		if (en) {
			ret = IMP_Encoder_StartRecvPic(index);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):IMP_Encoder_StartRecvPic failed\n", __func__, __LINE__);
				return -1;
			}
		} else {
			ret = IMP_Encoder_StopRecvPic(index);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):IMP_Encoder_StopRecvPic failed\n", __func__, __LINE__);
				return -1;
			}
		}
	} else if (!strncmp(pm->name, "Framesource", strlen("Framesource"))) {
		IMP_LOG_ERR(TAG, "%s: %s\n", pm->name, en?"ENABLE":"DISABLE");
		if (en) {
			ret = IMP_FrameSource_EnableChn(index);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_EnableChn failed\n", __func__, __LINE__);
				return -1;
			}
		} else {

			ret = IMP_FrameSource_DisableChn(index);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_DisableChn failed\n", __func__, __LINE__);
				return -1;
			}
		}
	} else {
		IMP_LOG_ERR(TAG, "%s: %s\n", pm->name, "NOACTION");
	}
	return 0;
}

int snap_pic(char *path, int format)
{
	int ret;
	FILE *fp;
	IMPFrameInfo *frame_bak;
	IMPFSChnAttr fs_chn_attr[2], fs_chn_attr_tmp;
	//uint32_t format = PIX_FMT_RAW;
	FSChannel *ch0 = NULL;
	FSChannel *ch1 = NULL;
	Module * pm_ch0 = NULL;
	Module * pm_ch1 = NULL;

	if (NULL == gFramesource) {
		return -1;
	}

	ch0 = &gFramesource->channel[0];
	ch1 = &gFramesource->channel[1];
	if (NULL == ch0->group) {
		IMP_LOG_ERR(TAG, "%s(%d): error FrameSource 0 do not enable\n", __func__, __LINE__);
		return -1;
	} else {
		pm_ch0 = ch0->group->module;
	}
	if (NULL == ch1->group) {
		IMP_LOG_ERR(TAG, "%s(%d):FrameSource 1 do not enable\n", __func__, __LINE__);
		pm_ch1 = NULL;
	} else {
		pm_ch1 = ch1->group->module;
	}

	fp = fopen(path, "wb");
	if(fp == NULL) {
		IMP_LOG_ERR(TAG, "%s(%d):open error !\n", __func__, __LINE__);
		return -1;
	}

	switch(format) {
		case PIX_FMT_NV12:
			break;
		case PIX_FMT_RAW:
			fs_chn_attr_tmp.pixFmt = (IMPPixelFormat)format;
			fs_chn_attr_tmp.nrVBs = 1;
			fs_chn_attr_tmp.type = FS_PHY_CHANNEL;
			break;

		case PIX_FMT_UYVY422:
			fs_chn_attr_tmp.pixFmt = (IMPPixelFormat)format;
			fs_chn_attr_tmp.nrVBs = 1;
			fs_chn_attr_tmp.type = FS_PHY_CHANNEL;
			fs_chn_attr_tmp.crop.enable = 0;
			fs_chn_attr_tmp.scaler.enable = 0;
			break;
		case PIX_FMT_YUYV422:
			fs_chn_attr_tmp.pixFmt = (IMPPixelFormat)format;
			fs_chn_attr_tmp.nrVBs = 1;
			fs_chn_attr_tmp.type = FS_PHY_CHANNEL;
			fs_chn_attr_tmp.crop.enable = 0;
			fs_chn_attr_tmp.scaler.enable = 0;
			break;
		case PIX_FMT_RGB565BE:
			fs_chn_attr_tmp.pixFmt = (IMPPixelFormat)format;
			fs_chn_attr_tmp.nrVBs = 1;
			fs_chn_attr_tmp.type = FS_PHY_CHANNEL;
			fs_chn_attr_tmp.crop.enable = 0;
			fs_chn_attr_tmp.scaler.enable = 0;
			break;
		case PIX_FMT_BGR0:
			fs_chn_attr_tmp.pixFmt = (IMPPixelFormat)format;
			fs_chn_attr_tmp.nrVBs = 1;
			fs_chn_attr_tmp.type = FS_PHY_CHANNEL;
			fs_chn_attr_tmp.crop.enable = 0;
			fs_chn_attr_tmp.scaler.enable = 0;
			break;
		case PIX_FMT_BGR24:
			fs_chn_attr_tmp.pixFmt = (IMPPixelFormat)format;
			fs_chn_attr_tmp.nrVBs = 1;
			fs_chn_attr_tmp.type = FS_PHY_CHANNEL;
			fs_chn_attr_tmp.crop.enable = 0;
			fs_chn_attr_tmp.scaler.enable = 0;
			break;
		default:
			IMP_LOG_ERR(TAG, "not support the format !\n");
			break;
	}

	if(format != PIX_FMT_NV12) {
		ret = tree_funcs_control(pm_ch0, 0);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "%s(%d):tree_funcs_control failed\n", __func__, __LINE__);
			return -1;
		}
		if (NULL != pm_ch1) {
			ret = tree_funcs_control(pm_ch1, 0);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):tree_funcs_control failed\n", __func__, __LINE__);
				return -1;
			}
		}
		ret = IMP_FrameSource_GetChnAttr(0, &fs_chn_attr[0]);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_GetChnAttr failed\n", __func__, __LINE__);
			return -1;
		}

		if (NULL != pm_ch1) {
			ret = IMP_FrameSource_GetChnAttr(1, &fs_chn_attr[1]);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_GetChnAttr failed\n", __func__, __LINE__);
				return -1;
			}
		}
		if(format == PIX_FMT_RAW) {
			ret = IMP_ISP_Tuning_SetISPBypass(IMPISP_TUNING_OPS_MODE_DISABLE);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):IMP_ISP_Tuning_SetISPBpass failed\n", __func__, __LINE__);
				return -1;
			}
		} else {
			fs_chn_attr_tmp.outFrmRateNum = fs_chn_attr[0].outFrmRateNum;
			fs_chn_attr_tmp.outFrmRateDen = fs_chn_attr[0].outFrmRateDen;
			fs_chn_attr_tmp.picWidth = fs_chn_attr[0].picWidth;
			fs_chn_attr_tmp.picHeight = fs_chn_attr[0].picHeight;
		}

		ret = IMP_FrameSource_SetChnAttr(0, &fs_chn_attr_tmp);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_SetChnAttr failed\n", __func__, __LINE__);
			return -1;
		}
		ret = IMP_FrameSource_EnableChn(0);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_EnableChn failed\n", __func__, __LINE__);
			return -1;
		}
	}

	ret = IMP_FrameSource_SetFrameDepth(0, 1);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_SetFrameDepth failed\n", __func__, __LINE__);
		return -1;
	}

	ret = IMP_FrameSource_GetFrame(0, &frame_bak);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_GetFrame failed\n", __func__, __LINE__);
		return -1;
	}


	fwrite((void *)frame_bak->virAddr, frame_bak->size, 1, fp);

	fclose(fp);
	IMP_FrameSource_ReleaseFrame(0, frame_bak);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_ReleaseFrame failed\n", __func__, __LINE__);
		return -1;
	}

	ret = IMP_FrameSource_SetFrameDepth(0, 0);
	if (ret < 0) {
		IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_SetFrameDepth failed\n", __func__, __LINE__);
		return -1;
	}

	if(format != PIX_FMT_NV12) {
		ret = IMP_FrameSource_DisableChn(0);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_DisableChn failed\n", __func__, __LINE__);
			return -1;
		}

		if(format == PIX_FMT_RAW) {
			ret = IMP_ISP_Tuning_SetISPBypass(IMPISP_TUNING_OPS_MODE_ENABLE);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):IMP_ISP_Tuning_SetISPBpass failed\n", __func__, __LINE__);
				return -1;
			}
		}

		ret = IMP_FrameSource_SetChnAttr(0, &fs_chn_attr[0]);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_SetChnAttr failed\n", __func__, __LINE__);
			return -1;
		}

		if (NULL != pm_ch1) {
			ret = IMP_FrameSource_SetChnAttr(1, &fs_chn_attr[1]);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):IMP_FrameSource_SetChnAttr failed\n", __func__, __LINE__);
				return -1;
			}
		}
		ret = tree_funcs_control(pm_ch0, 1);
		if (ret < 0) {
			IMP_LOG_ERR(TAG, "%s(%d):tree_funcs_control failed\n", __func__, __LINE__);
			return -1;
		}
		if (NULL != pm_ch1) {
			ret = tree_funcs_control(pm_ch1, 1);
			if (ret < 0) {
				IMP_LOG_ERR(TAG, "%s(%d):tree_funcs_control failed\n", __func__, __LINE__);
				return -1;
			}
		}
	}
	return 0;
}

#define CHNNUM_MAX 32
struct FS_Pool
{
	int pools[CHNNUM_MAX]; //key chnNum, val poolId
};

static struct FS_Pool *g_pools;

int IMP_FrameSource_SetPool(int chnNum, int poolID)
{
	if(chnNum < 0 || chnNum > CHNNUM_MAX) {
		IMP_LOG_ERR(TAG, "%s(%d):chnNum(%d) error\n", __func__, __LINE__, chnNum);
		return -1;
	}
	if(!IMP_MemPool_GetById(poolID)) {
		IMP_LOG_ERR(TAG, "%s(%d):POOL is not init\n", __func__, __LINE__);
		return -1;
	}
	if(!g_pools) {
		g_pools = (struct FS_Pool *)malloc(sizeof(struct FS_Pool));
		if(!g_pools) {
			IMP_LOG_ERR(TAG, "%s(%d):malloc pool error\n", __func__, __LINE__);
			return -1;
		}
		memset(g_pools, -1, sizeof(struct FS_Pool));
	}

	if(g_pools->pools[chnNum] >= 0) {
		IMP_LOG_ERR(TAG, "%s(%d):pools already set\n", __func__, __LINE__);
		return -1;
	}
	g_pools->pools[chnNum] = poolID;
	return 0;
}

int IMP_FrameSource_GetPool(int chnNum)
{
	if(chnNum < 0 || chnNum > CHNNUM_MAX) {
		IMP_LOG_ERR(TAG, "%s(%d):chnNum(%d) error\n", __func__, __LINE__, chnNum);
		return -1;
	}

	if(!g_pools || g_pools->pools[chnNum] == -1) {
		IMP_LOG_ERR(TAG, "%s(%d):chnNum: %d not bind pool\n", __func__, __LINE__, chnNum);
		return -1;
	}
	return g_pools->pools[chnNum];
}

int IMP_FrameSource_SetDirectModeAttr(int chnNum, int data_threshold)
{
	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	Framesource *framesource = get_framesource();
	if (framesource == NULL) {
		IMP_LOG_ERR(TAG, "%s():[chn%d] FrameSource is invalid,"
					"maybe system was not inited yet.\n", __func__, chnNum);
		return -1;
	}

	FSChannel *chan = &framesource->channel[chnNum];
	if (FSCHANNEL_STATE_RUN != chan->state) {
		IMP_LOG_ERR(TAG, "%s(%d): channel %d should run first\n", __func__, __LINE__,  chnNum);
		return -1;
	}

	chan->data_threshold = data_threshold;

	IMP_LOG_INFO(TAG, "%s():[chn%d] data_threshold (%d) is successfully setted\n ", __func__, chnNum, chan->data_threshold);
	return 0;
}

int IMP_FrameSource_GetDirectModeAttr(int chnNum, int *data_threshold)
{
	if (chnNum > (NR_MAX_FS_CHNS - 1)) {
		IMP_LOG_ERR(TAG, "%s(): Invalid chnNum %d\n", __func__, chnNum);
		return -1;
	}

	Framesource *framesource = get_framesource();
	if (framesource == NULL) {
		IMP_LOG_ERR(TAG, "%s():[chn%d] FrameSource is invalid,"
					"maybe system was not inited yet.\n", __func__, chnNum);
		return -1;
	}

	FSChannel *chan = &framesource->channel[chnNum];
	if (FSCHANNEL_STATE_RUN != chan->state) {
		IMP_LOG_ERR(TAG, "%s(%d): channel %d should run first\n", __func__, __LINE__,  chnNum);
		return -1;
	}

	*data_threshold = chan->data_threshold;

	return 0;
}
